Skip to main content
Glama
bradcstevens

Copilot Studio Agent Direct Line MCP Server

by bradcstevens

start_conversation

Initiate a new conversation with a Microsoft Copilot Studio Agent to interact with custom AI assistants through Direct Line 3.0 API integration.

Instructions

Start a new conversation with the Copilot Studio Agent

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
initialMessageNoOptional first message to send

Implementation Reference

  • Core handler function that executes the start_conversation tool: validates args, creates conversation via ConversationManager, optionally sends initial message and polls for bot response.
    private async handleStartConversation(
      args: Record<string, unknown>,
      userContext?: UserContext
    ) {
      const { initialMessage } = validateToolArgs(StartConversationArgsSchema, args);
    
      try {
        // Create new conversation with user-specific client ID
        const clientId = userContext
          ? `user-${userContext.userId}-${Date.now()}`
          : `mcp-client-${Date.now()}`;
        const convState = await this.conversationManager.createConversation(clientId);
    
        // Associate conversation with user
        if (userContext) {
          this.associateConversationWithUser(userContext.userId, convState.conversationId);
        }
    
        let result: {
          conversationId: string;
          status: string;
          response?: string;
          activityId?: string;
        } = {
          conversationId: convState.conversationId,
          status: 'started',
        };
    
        // If initial message provided, send it
        if (initialMessage && typeof initialMessage === 'string') {
          const activityId = await this.client.sendActivity(
            {
              conversationId: convState.conversationId,
              activity: {
                type: 'message',
                from: { id: clientId, name: userContext?.name || 'MCP User' },
                text: initialMessage,
                timestamp: new Date().toISOString(),
                channelData: userContext
                  ? {
                      userId: userContext.userId,
                      userEmail: userContext.email,
                      tenantId: userContext.tenantId,
                    }
                  : undefined,
              },
            },
            convState.token
          );
    
          // Poll for response (same logic as send_message)
          const startTime = Date.now();
          const timeout = 30000;
          let botResponse = '';
    
          while (Date.now() - startTime < timeout) {
            await new Promise((resolve) => setTimeout(resolve, 1000));
    
            const activitySet = await this.client.getActivities(
              {
                conversationId: convState.conversationId,
                watermark: convState.watermark,
              },
              convState.token
            );
    
            if (activitySet.watermark) {
              this.conversationManager.updateWatermark(convState.conversationId, activitySet.watermark);
            }
    
            const botActivities = activitySet.activities.filter(
              (a) => a.type === 'message' && a.from?.id !== clientId
            );
    
            if (botActivities.length > 0) {
              botActivities.forEach((activity) => {
                this.conversationManager.addToHistory(convState.conversationId, activity);
              });
    
              const latestBot = botActivities[botActivities.length - 1];
              botResponse = latestBot.text || '[No text response]';
              break;
            }
          }
    
          result.response = botResponse || '[No response received within timeout period]';
          result.activityId = activityId;
        }
    
        // Audit log
        this.logAudit({
          timestamp: Date.now(),
          userId: userContext?.userId,
          action: 'start_conversation',
          conversationId: convState.conversationId,
        });
    
        return createSuccessResponse(result);
      } catch (error) {
        throw new Error(
          `Failed to start conversation: ${error instanceof Error ? error.message : String(error)}`
        );
      }
    }
  • Zod schema defining input validation for start_conversation tool arguments (optional initialMessage).
    export const StartConversationArgsSchema = z.object({
      initialMessage: z.string().min(1, 'Initial message cannot be empty').optional(),
    });
    
    export type StartConversationArgs = z.infer<typeof StartConversationArgsSchema>;
  • Tool registration in ListToolsRequestHandler for stdio transport, defining name, description, and input schema.
      name: 'start_conversation',
      description: 'Start a new conversation with the Copilot Studio Agent',
      inputSchema: {
        type: 'object',
        properties: {
          initialMessage: {
            type: 'string',
            description: 'Optional first message to send',
          },
        },
      },
    },
  • Tool registration in HTTP transport handler for tools/list method.
    name: 'start_conversation',
    description: 'Start a new conversation with the Copilot Studio Agent',
    inputSchema: {
      type: 'object',
      properties: {
        initialMessage: {
          type: 'string',
          description: 'Optional first message to send',
        },
      },
    },
  • Helper method called by handler to create and manage conversation state, delegating actual DirectLine start to client.
    async createConversation(clientId: string): Promise<ConversationState> {
      // Get token from token manager
      const token = await this.tokenManager.getToken(clientId);
    
      // Start conversation with Direct Line
      const conversation = await this.client.startConversation(token);
    
      // Create state
      const state: ConversationState = {
        conversationId: conversation.conversationId,
        token: conversation.token,
        clientId,
        watermark: undefined,
        createdAt: Date.now(),
        lastActivity: Date.now(),
        messageHistory: [],
      };
    
      // Store state
      this.conversations.set(conversation.conversationId, state);
    
      // Update metrics
      this.metrics.totalCreated++;
      this.metrics.activeCount = this.conversations.size;
    
      // Schedule cleanup
      this.scheduleCleanup(conversation.conversationId);
    
      console.error(`[ConversationManager] Created conversation ${conversation.conversationId} for client ${clientId}`);
    
      return state;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bradcstevens/copilot-studio-agent-direct-line-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server