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
2 changes: 1 addition & 1 deletion packages/agents-a365-runtime/src/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class Utility {
* @param orchestrator Optional orchestrator identifier to include in the User-Agent string.
* @returns Formatted User-Agent header string.
*/
public static GetUserAgentHeader(orchestrator: string = ''): string {
public static GetUserAgentHeader(orchestrator?: string): string {
const osType = os.type();
const orchestratorPart = orchestrator ? `; ${orchestrator}` : '';
return `Agent365SDK/${LIB_VERSION} (${osType}; Node.js ${process.version}${orchestratorPart})`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { McpToolServerConfigurationService, McpClientTool, Utility, MCPServerConfig } from '@microsoft/agents-a365-tooling';
import { McpToolServerConfigurationService, McpClientTool, Utility, MCPServerConfig, ToolOptions } from '@microsoft/agents-a365-tooling';
import { AgenticAuthenticationService, Utility as RuntimeUtility } from '@microsoft/agents-a365-runtime';

// Agents SDK
Expand All @@ -16,6 +16,7 @@ import type { McpServerConfig, Options } from '@anthropic-ai/claude-agent-sdk';
*/
export class McpToolRegistrationService {
private readonly configService: McpToolServerConfigurationService = new McpToolServerConfigurationService();
private readonly orchestratorName: string = "Claude";

/**
* Registers MCP tool servers and updates agent options with discovered tools and server configs.
Expand Down Expand Up @@ -46,12 +47,13 @@ export class McpToolRegistrationService {
Utility.ValidateAuthToken(authToken);

const agenticAppId = RuntimeUtility.ResolveAgentIdentity(turnContext, authToken);
const servers = await this.configService.listToolServers(agenticAppId, authToken);
const options: ToolOptions = { orchestratorName: this.orchestratorName, turnContext: turnContext };
const servers = await this.configService.listToolServers(agenticAppId, authToken, options);
const mcpServers: Record<string, McpServerConfig> = {};
const tools: McpClientTool[] = [];

for (const server of servers) {
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, turnContext);
for (const server of servers) {
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, options);

// Add each server to the config object
mcpServers[server.mcpServerName] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

// Microsoft Agent 365 SDK
import { McpToolServerConfigurationService, Utility } from '@microsoft/agents-a365-tooling';
import { McpToolServerConfigurationService, Utility, ToolOptions } from '@microsoft/agents-a365-tooling';
import { AgenticAuthenticationService, Utility as RuntimeUtility } from '@microsoft/agents-a365-runtime';

// Agents SDK
Expand All @@ -18,6 +18,7 @@ import { ClientConfig, Connection, MultiServerMCPClient } from '@langchain/mcp-a
*/
export class McpToolRegistrationService {
private configService: McpToolServerConfigurationService = new McpToolServerConfigurationService();
private readonly orchestratorName: string = "LangChain";

/**
* Registers MCP tool servers and updates agent options with discovered tools and server configs.
Expand Down Expand Up @@ -49,12 +50,13 @@ export class McpToolRegistrationService {
Utility.ValidateAuthToken(authToken);

const agenticAppId = RuntimeUtility.ResolveAgentIdentity(turnContext, authToken);
const servers = await this.configService.listToolServers(agenticAppId, authToken);
const options: ToolOptions = { orchestratorName: this.orchestratorName, turnContext: turnContext };
const servers = await this.configService.listToolServers(agenticAppId, authToken, options);
const mcpServers: Record<string, Connection> = {};

for (const server of servers) {
// Compose headers if values are available
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, turnContext);
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, options);

// Create Connection instance for LangChain agents
mcpServers[server.mcpServerName] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { McpToolServerConfigurationService, Utility } from '@microsoft/agents-a365-tooling';
import { McpToolServerConfigurationService, Utility, ToolOptions } from '@microsoft/agents-a365-tooling';
import { AgenticAuthenticationService, Utility as RuntimeUtility } from '@microsoft/agents-a365-runtime';

// Agents SDK
Expand All @@ -16,6 +16,7 @@ import { Agent, MCPServerStreamableHttp } from '@openai/agents';
*/
export class McpToolRegistrationService {
private configService: McpToolServerConfigurationService = new McpToolServerConfigurationService();
private readonly orchestratorName: string = "OpenAI";


/**
Expand Down Expand Up @@ -48,12 +49,13 @@ export class McpToolRegistrationService {
Utility.ValidateAuthToken(authToken);

const agenticAppId = RuntimeUtility.ResolveAgentIdentity(turnContext, authToken);
const servers = await this.configService.listToolServers(agenticAppId, authToken);
const options: ToolOptions = { orchestratorName: this.orchestratorName, turnContext: turnContext };
const servers = await this.configService.listToolServers(agenticAppId, authToken, options);
const mcpServers: MCPServerStreamableHttp[] = [];

for (const server of servers) {
// Compose headers if values are available
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, turnContext);
const headers: Record<string, string> = Utility.GetToolRequestHeaders(authToken, options);

// Create MCPServerStreamableHttp instance for OpenAI agents
const mcpServer = new MCPServerStreamableHttp({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import { MCPServerConfig, McpClientTool } from './contracts';
import { MCPServerConfig, McpClientTool, ToolOptions } from './contracts';
import { Utility } from './Utility';

import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
Expand All @@ -30,8 +30,21 @@ export class McpToolServerConfigurationService {
* @param authToken Optional bearer token used when querying the remote tooling gateway.
* @returns A promise resolving to an array of normalized MCP server configuration objects.
*/
async listToolServers(agenticAppId: string, authToken: string): Promise<MCPServerConfig[]> {
return await (this.isDevScenario() ? this.getMCPServerConfigsFromManifest() : this.getMCPServerConfigsFromToolingGateway(agenticAppId, authToken));
async listToolServers(agenticAppId: string, authToken: string): Promise<MCPServerConfig[]>;

/**
* Return MCP server definitions for the given agent. In development (NODE_ENV=Development) this reads the local ToolingManifest.json; otherwise it queries the remote tooling gateway.
*
* @param agenticAppId The agentic app id for which to discover servers.
* @param authToken Optional bearer token used when querying the remote tooling gateway.
* @param options Optional tool options when calling the gateway.
* @returns A promise resolving to an array of normalized MCP server configuration objects.
*/
async listToolServers(agenticAppId: string, authToken: string, options?: ToolOptions): Promise<MCPServerConfig[]>;

async listToolServers(agenticAppId: string, authToken: string, options?: ToolOptions): Promise<MCPServerConfig[]> {
return await (this.isDevScenario() ? this.getMCPServerConfigsFromManifest() :
this.getMCPServerConfigsFromToolingGateway(agenticAppId, authToken, options));
}

/**
Expand Down Expand Up @@ -74,9 +87,10 @@ export class McpToolServerConfigurationService {
*
* @param agenticAppId The agentic app id used by the tooling gateway to scope results.
* @param authToken Optional Bearer token to include in the Authorization header when calling the gateway.
* @param options Optional tool options when calling the gateway.
* @throws Error when the gateway call fails or returns an unexpected payload.
*/
private async getMCPServerConfigsFromToolingGateway(agenticAppId: string, authToken: string): Promise<MCPServerConfig[]> {
private async getMCPServerConfigsFromToolingGateway(agenticAppId: string, authToken: string, options?: ToolOptions): Promise<MCPServerConfig[]> {
// Validate the authentication token
Utility.ValidateAuthToken(authToken);

Expand All @@ -86,9 +100,7 @@ export class McpToolServerConfigurationService {
const response = await axios.get(
configEndpoint,
{
headers: {
'Authorization': authToken ? `Bearer ${authToken}` : undefined,
},
headers: Utility.GetToolRequestHeaders(authToken, options),
timeout: 10000 // 10 seconds timeout
}
);
Expand Down
40 changes: 37 additions & 3 deletions packages/agents-a365-tooling/src/Utility.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { TurnContext } from '@microsoft/agents-hosting';

Check failure on line 4 in packages/agents-a365-tooling/src/Utility.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK (18)

'TurnContext' is defined but never used. Allowed unused vars must match /^_/u

Check failure on line 4 in packages/agents-a365-tooling/src/Utility.ts

View workflow job for this annotation

GitHub Actions / Node.js SDK (20)

'TurnContext' is defined but never used. Allowed unused vars must match /^_/u
import { Utility as RuntimeUtility } from '@microsoft/agents-a365-runtime';

import { ToolOptions } from './contracts';

// Constant for MCP Platform base URL in production
const MCP_PLATFORM_PROD_BASE_URL = 'https://agent365.svc.cloud.microsoft';

export class Utility {
public static readonly HEADER_CHANNEL_ID = 'x-ms-channel-id';
public static readonly HEADER_SUBCHANNEL_ID = 'x-ms-subchannel-id';
public static readonly HEADER_USER_AGENT = 'User-Agent';
public static readonly HEADER_AGENT_BLUEPRINT_ID = 'x-ms-agent-blueprint-id';
public static readonly HEADER_AGENT_INSTANCE_ID = 'x-ms-agent-instance-id';
public static readonly HEADER_AGENT_USER_ID = 'x-ms-agent-user-id'
public static readonly HEADER_USER_ROLE = 'x-ms-user-role';

/**
* Compose standard headers for MCP tooling requests.
* Includes Authorization bearer token when provided, and optionally includes channel and subchannel identifiers for routing.
*
* @param authToken Bearer token for Authorization header.
* @param turnContext Optional TurnContext object from which channel and subchannel IDs are extracted.
* @param options Optional ToolOptions object for additional request configuration.
* @returns A headers record suitable for HTTP requests.
*/
public static GetToolRequestHeaders(
authToken?: string,
turnContext?: TurnContext
options?: ToolOptions
): Record<string, string> {
const headers: Record<string, string> = {};

if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}

const channelId = turnContext?.activity?.channelId as string | undefined;
const subChannelId = turnContext?.activity?.channelIdSubChannel as string | undefined;
const activity = options?.turnContext?.activity;

const agentBlueprintId = activity?.recipient?.agenticAppBlueprintId;
if (agentBlueprintId) {
headers[Utility.HEADER_AGENT_BLUEPRINT_ID] = agentBlueprintId;
}

const agentIntanceId = activity?.recipient?.agenticAppId;
if (agentIntanceId) {
headers[Utility.HEADER_AGENT_INSTANCE_ID] = agentIntanceId;
}

const agentUserId = activity?.recipient?.agenticUserId
if (agentUserId) {
headers[Utility.HEADER_AGENT_USER_ID] = agentUserId;
}

const userRole = activity?.recipient?.role;
if (userRole) {
headers[Utility.HEADER_USER_ROLE] = userRole;
}

const channelId = activity?.channelId;
if (channelId) {
headers[Utility.HEADER_CHANNEL_ID] = channelId;
}

const subChannelId = activity?.channelIdSubChannel as string | undefined;
if (subChannelId) {
headers[Utility.HEADER_SUBCHANNEL_ID] = subChannelId;
}

if (options?.orchestratorName) {
headers[Utility.HEADER_USER_AGENT] = RuntimeUtility.GetUserAgentHeader(options.orchestratorName);
}

return headers;
}

Expand Down
7 changes: 7 additions & 0 deletions packages/agents-a365-tooling/src/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { TurnContext } from "@microsoft/agents-hosting";

export interface MCPServerConfig {
mcpServerName: string;
url: string;
Expand All @@ -18,4 +20,9 @@ export interface InputSchema {
properties?: Record<string, object>;
required?: string[];
additionalProperties?: boolean;
}

export interface ToolOptions {
orchestratorName?: string;
turnContext?: TurnContext;
}
Loading