Skip to content

Commit 134852e

Browse files
feat: add protocol logging UI controls and endpoint
- Add protocol logging checkbox to testing UI - Add /api/sales/agents/:agentId/query endpoint with protocol logging support - Export ProtocolLoggingConfig type from library - Wire-level HTTP logs now display in debug panel when enabled
1 parent e63c375 commit 134852e

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

src/lib/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ export type {
157157
// Re-export all Zod schemas for user validation needs
158158
export * from './types/schemas.generated';
159159

160+
// ====== PROTOCOL LOGGING ======
161+
// Protocol logging configuration
162+
export type { ProtocolLoggingConfig } from './protocols/mcp';
163+
160164
// ====== AUTHENTICATION ======
161165
// Auth utilities for custom integrations
162166
export { getAuthToken, createAdCPHeaders, createMCPAuthHeaders, createAuthenticatedFetch } from './auth';

src/public/index.html

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,6 +2249,12 @@ <h1>Ad Context Protocol Testing Framework</h1>
22492249
📋 View All
22502250
</button>
22512251
</div>
2252+
<div style="display: flex; align-items: center; gap: 5px; padding: 8px 12px; background: rgba(255,255,255,0.1); border-radius: 6px">
2253+
<input type="checkbox" id="enable-protocol-logging" style="width: auto; margin: 0" />
2254+
<label for="enable-protocol-logging" style="margin: 0; cursor: pointer; font-size: 13px" title="Enable detailed wire-level HTTP request/response logging">
2255+
🔍 Protocol Logging
2256+
</label>
2257+
</div>
22522258
</div>
22532259
</div>
22542260

@@ -8362,6 +8368,23 @@ <h4>📥 Response</h4>
83628368
}
83638369
}
83648370

8371+
// Helper function to get protocol logging config from UI
8372+
function getProtocolLoggingConfig() {
8373+
const enabled = document.getElementById('enable-protocol-logging').checked;
8374+
if (!enabled) {
8375+
return null;
8376+
}
8377+
return {
8378+
enabled: true,
8379+
logRequests: true,
8380+
logResponses: true,
8381+
logRequestBodies: true,
8382+
logResponseBodies: true,
8383+
maxBodySize: 50000,
8384+
redactAuthHeaders: true,
8385+
};
8386+
}
8387+
83658388
async function executeMCPTool(agent, toolName, params) {
83668389
logEvent('info', `Executing MCP tool ${toolName} with agent: ${agent.name} (ID: ${agent.id})`);
83678390
return await executeMCPToolBackend(agent, toolName, params);
@@ -8406,6 +8429,7 @@ <h4>📥 Response</h4>
84068429
brandStory: params.brief || 'Test execution from ADCP Testing Suite',
84078430
offering: null,
84088431
agentConfig: agent,
8432+
protocolLogging: getProtocolLoggingConfig(),
84098433
}),
84108434
});
84118435

@@ -8486,6 +8510,7 @@ <h4>📥 Response</h4>
84868510
brandStory: params.brief || 'Test execution from ADCP Testing Suite',
84878511
offering: null,
84888512
agentConfig: agent,
8513+
protocolLogging: getProtocolLoggingConfig(),
84898514
}),
84908515
});
84918516

src/server/server.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
type SyncCreativesResponse,
2020
type CreateMediaBuyResponse,
2121
type MediaBuyDeliveryNotification,
22+
type ProtocolLoggingConfig,
2223
ADCP_STATUS,
2324
InputRequiredError,
2425
} from '../lib';
@@ -127,6 +128,18 @@ const clientConfig: SingleAgentClientConfig = {
127128
logSchemaViolations: process.env.ADCP_LOG_SCHEMA_VIOLATIONS !== 'false', // Default: true
128129
},
129130

131+
// Protocol logging configuration - wire-level HTTP request/response logging
132+
// Can be enabled via env var or dynamically per request
133+
protocolLogging: {
134+
enabled: process.env.ADCP_PROTOCOL_LOGGING === 'true', // Default: false
135+
logRequests: true,
136+
logResponses: true,
137+
logRequestBodies: true,
138+
logResponseBodies: true,
139+
maxBodySize: 50000,
140+
redactAuthHeaders: true,
141+
},
142+
130143
// Activity logging - store ALL events
131144
onActivity: (activity: Activity) => {
132145
storeEvent({
@@ -660,6 +673,89 @@ app.get('/api/sales/agents/:agentId/info', async (request, reply) => {
660673
}
661674
});
662675

676+
// Query agent endpoint - supports protocol logging
677+
app.post<{
678+
Params: { agentId: string };
679+
Body: {
680+
brandStory?: string;
681+
offering?: string | null;
682+
agentConfig?: AgentConfig;
683+
protocolLogging?: ProtocolLoggingConfig | null;
684+
};
685+
}>('/api/sales/agents/:agentId/query', async (request, reply) => {
686+
const { agentId } = request.params;
687+
const { brandStory, offering, agentConfig, protocolLogging } = request.body;
688+
689+
try {
690+
// Create a temporary client if custom config is provided, or use the existing client with protocol logging
691+
let agent;
692+
let tempClient: ADCPMultiAgentClient | null = null;
693+
694+
if (agentConfig) {
695+
// Custom agent configuration provided
696+
const customAgentConfig: AgentConfig = {
697+
id: agentConfig.id || agentId,
698+
name: agentConfig.name || agentId,
699+
agent_uri: agentConfig.agent_uri || (agentConfig as any).server_url,
700+
protocol: agentConfig.protocol || 'mcp',
701+
auth_token_env: agentConfig.auth_token_env,
702+
requiresAuth: agentConfig.requiresAuth !== false,
703+
};
704+
705+
// Merge protocol logging config if provided
706+
const tempClientConfig: SingleAgentClientConfig = {
707+
...clientConfig,
708+
...(protocolLogging && { protocolLogging }),
709+
};
710+
711+
tempClient = new ADCPMultiAgentClient([customAgentConfig], tempClientConfig);
712+
agent = tempClient.agent(customAgentConfig.id);
713+
} else {
714+
// Use existing client - if protocol logging is provided, create new client with that config
715+
if (protocolLogging) {
716+
const agentConfigs = adcpClient.getAgentConfigs();
717+
const tempClientConfig: SingleAgentClientConfig = {
718+
...clientConfig,
719+
protocolLogging,
720+
};
721+
tempClient = new ADCPMultiAgentClient(agentConfigs, tempClientConfig);
722+
agent = tempClient.agent(agentId);
723+
} else {
724+
agent = adcpClient.agent(agentId);
725+
}
726+
}
727+
728+
if (!agent) {
729+
return reply.code(404).send({
730+
success: false,
731+
error: `Agent ${agentId} not found`,
732+
timestamp: new Date().toISOString(),
733+
});
734+
}
735+
736+
// Call get_products by default (this is what the testing UI expects)
737+
const result = await agent.getProducts({
738+
brand_manifest: { name: brandStory || offering || 'Test brand' },
739+
...(brandStory && { brief: brandStory }),
740+
});
741+
742+
return reply.send({
743+
success: result.success,
744+
data: result.data,
745+
error: result.error,
746+
debugLogs: result.debug_logs || [],
747+
timestamp: new Date().toISOString(),
748+
});
749+
} catch (error) {
750+
app.log.error(`Failed to query agent ${agentId}: ${error instanceof Error ? error.message : String(error)}`);
751+
return reply.code(500).send({
752+
success: false,
753+
error: error instanceof Error ? error.message : 'Failed to query agent',
754+
timestamp: new Date().toISOString(),
755+
});
756+
}
757+
});
758+
663759
// Helper function to extract data from nested A2A/MCP responses
664760
function extractResponseData(result: any): any {
665761
// Check multiple possible nested structures

0 commit comments

Comments
 (0)