Skip to content

feat(inspector): primitive execution & always-visible event logs - #160

Merged
gabrypavanello merged 12 commits into
mainfrom
feat/primitive-execution
Feb 9, 2026
Merged

feat(inspector): primitive execution & always-visible event logs#160
gabrypavanello merged 12 commits into
mainfrom
feat/primitive-execution

Conversation

@gabe4coding

Copy link
Copy Markdown
Contributor

Summary

  • Backend: Single POST /api/execute-primitive endpoint dispatching to callTool/readResource/getPrompt by kind field, with 30s configurable timeout and manual event recording
  • Frontend: executePrimitive utility with per-kind response mappers (mapToolResponse, mapResourceResponse, mapPromptResponse), wired through InspectorDashboardMcpPrimitivesPanelPrimitiveDetail as onExecute prop
  • UI: Browse-mode action button enabled when onExecute provided (switches to action mode), RightPanel always renders all 3 tabs (Agent/Events/Logs) with isStreaming prop removed entirely
  • Events: 6 manual event types added to type system (manual_tool_call, manual_tool_result, etc.) with source: "manual" badge in AgentPanel
  • Tests: 105 new tests covering mappers, endpoint contract, manual event types, and RightPanel behavior

Acceptance Criteria

  • AC-01: Backend /api/execute-primitive endpoint routing through ConnectionRegistry by connectionId
  • AC-02: Frontend executePrimitive utility + onExecute wired through component tree
  • AC-03: Browse-mode action button enabled when onExecute provided; in-form Run button enabled when required inputs filled
  • AC-04: ResponsePanel shows results below form after execution
  • AC-05: Agent panel shows manual calls tagged source:manual (visually distinct via badge)
  • AC-06: Right panel: all 3 tabs always visible regardless of streaming state; isStreaming prop removed
  • AC-07: connectionId (selectedPrimitive.serverId) passed through executePrimitive to /api/execute-primitive
  • AC-08: Per-kind mapper functions produce ExecutionResult shape
  • AC-09: Request timeout configurable (default 30s) — on timeout, error shown in ResponsePanel
  • AC-10: Manual events include: primitive kind, name, input params, connectionId, response status, duration, source:manual

Test plan

  • Run pnpm -C packages/inspector test — 1621 tests should pass
  • Start inspector, connect to an MCP server, select a tool → verify "Run" button appears and works
  • Execute a tool/resource/prompt → verify results appear in ResponsePanel
  • Check Agent panel → verify manual calls show with "MANUAL" badge
  • Verify all 3 right panel tabs are always visible (no streaming gate)

🤖 Generated with Claude Code

gabrypavanello and others added 11 commits February 9, 2026 10:41
…dpoint

- Add "manual" to AgnosticInspectorEvent.source union
- Add 6 manual event types (manual_tool_call, manual_tool_result, etc.)
- Map manual events to "agent" category in getEventCategory/getEventSummary
- Add POST /api/execute-primitive endpoint dispatching by kind (tool/resource/prompt)
- Record manual events before/after execution with source: "manual"
- 30s default timeout, overrideable via request body
- Extend recordAgentEvent with optional source parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove isStreaming from RightPanelProps and all usages
- Always render Agent, Events, and Logs tabs
- Replace handleClear/isClearDisabled with switch(activeTab) dispatch
- Update right-panel tests for always-visible behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ided

- Action button enabled with primary style when onExecute prop exists
- onClick switches to action mode (shows form), does not execute immediately
- Remains disabled with "Coming soon" when onExecute is absent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mappers

- mapToolResponse: maps tool call results to ExecutionResult (ok=!isError)
- mapResourceResponse: maps resource read results with uri+mimeType
- mapPromptResponse: maps prompt messages with role/content handling
- executePrimitive: fetches /api/execute-primitive, dispatches to correct mapper
- All mappers never throw, always return ExecutionResult

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…veDetail

- Create handleExecutePrimitive in InspectorDashboard binding serverId as connectionId
- Add onExecute prop to McpPrimitivesPanel and thread to PrimitiveDetail
- selectedPrimitive.serverId used as connectionId for execute-primitive API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show "MANUAL" badge on events with source: "manual"
- Add manualBadge styles (amber accent color)
- Manual events appear in Agent panel with visual distinction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- mapToolResponse: success content, error flag, empty data
- mapResourceResponse: contents mapping, empty data
- mapPromptResponse: messages with roles, empty data
- executePrimitive: success flow, network error, non-200 response

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ugh component tree

- executePrimitive with per-kind mappers (tool/resource/prompt)
- createExecuteFn binds baseUrl + connectionId for ExecuteFn signature
- Wire onExecute through InspectorDashboard -> McpPrimitivesPanel -> PrimitiveDetail
- Thread baseUrl through to fetch calls (consistent with other hooks)
- 22 unit tests for mappers and executePrimitive function

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap Promise.race in try/finally to clear setTimeout on success
- Prevents timer leak on successful executions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backend /api/execute-primitive endpoint integration tests
- Manual event type mapping tests (getEventCategory, getEventSummary)
- VALID_INSPECTOR_EVENT_TYPES validation for all 6 manual types
- 83 additional tests for comprehensive coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Execute tools, resources, and prompts directly from the inspector dashboard with real-time feedback
    • Manual executions tracked and labeled with "manual" badges in the event log
  • UI/UX Improvements

    • Simplified right panel with consistent tab visibility and navigation

Walkthrough

This PR introduces a manual primitive execution feature enabling users to execute tools, resources, and prompts directly from the inspector dashboard. It adds a backend /api/execute-primitive endpoint, frontend execution utilities with response mappers, new event types for tracking manual executions, UI updates to show execute buttons and manual event badges, and simplifies RightPanel by removing the isStreaming prop to always display three tabs.

Changes

Cohort / File(s) Summary
Manual Event Type System
packages/inspector/src/types/inspector-event-types.ts
Adds six new manual_* event types (tool_call, tool_result, resource_read, resource_result, prompt_get, prompt_result), extends AgnosticInspectorEvent.source to include "manual", and updates getEventCategory and getEventSummary functions to classify and describe manual execution events.
Backend Execution Endpoint
packages/inspector/src/standalone-server.ts
Implements POST /api/execute-primitive endpoint handling tool, resource, and prompt execution requests. Includes validation, connection resolution, pre/post-execution event recording, error handling, and timeout management.
Frontend Execution Utilities
packages/inspector/src/dashboard/react/utils/executePrimitive.ts
Provides executePrimitive function to trigger primitives via API and response mapper functions (mapToolResponse, mapResourceResponse, mapPromptResponse) that normalize disparate backend responses into ExecutionResult. Exports curried createExecuteFn for UI component binding.
Dashboard Integration
packages/inspector/src/dashboard/react/InspectorDashboard.tsx, packages/inspector/src/dashboard/react/components/McpPrimitivesPanel.tsx
Introduces handleExecutePrimitive memoized binding and passes onExecute callback to MCPPrimitivesPanel. McpPrimitivesPanel accepts onExecute prop and propagates to PrimitiveDetail via ServerBlocksContent.
Primitive Execution UI
packages/inspector/src/dashboard/react/components/PrimitiveDetail.tsx
Conditionally renders enabled "Execute" action button when onExecute is provided (switches mode to "action"), or disabled "Coming soon" button when absent. Enables user interaction to initiate primitive execution.
Event Visualization
packages/inspector/src/dashboard/react/components/EventRow.tsx, packages/inspector/src/dashboard/react/styles.ts
Adds manual event badge to EventRow when event.source === "manual". Introduces eventBadgeManual style with distinct typography and appearance for manual-source events.
RightPanel Simplification
packages/inspector/src/dashboard/react/components/RightPanel.tsx
Removes isStreaming prop and conditional rendering logic. Always displays three tabs (Agent, Events, Logs) with uniform styling. Consolidates clear button behavior via activeTab switch instead of streaming-based logic.
Connection Management
packages/inspector/src/connection.ts
Adds optional source parameter to recordAgentEvent method, allowing event.source to default to provided source or "agent".
Build Configuration
.gitignore
Adds /board/ to ignore list.
Comprehensive Test Coverage
packages/inspector/tests/execute-primitive*.test.ts, packages/inspector/tests/manual-event-types.test.ts, packages/inspector/tests/right-panel-agent-only.test.ts
Adds 1,786 lines of test coverage including: execute-primitive endpoint and utility function tests; manual event type classification and summary tests; RightPanel three-tab model tests.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Dashboard as Inspector Dashboard
    participant API as Backend API
    participant MCP as MCP Client
    participant EventSys as Event System

    User->>Dashboard: Click Execute Button
    Dashboard->>API: POST /api/execute-primitive<br/>(kind, name, params, connectionId)
    API->>EventSys: Record manual_*_call event
    API->>MCP: Execute primitive<br/>(callTool / readResource / getPrompt)
    MCP-->>API: Primitive result or error
    API->>EventSys: Record manual_*_result event
    API-->>Dashboard: Execution result + duration
    Dashboard->>Dashboard: Display result in UI
    Dashboard-->>User: Show execution outcome
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: primitive execution capability and always-visible event logs in the inspector.
Description check ✅ Passed The description is detailed and directly related to the changeset, covering backend/frontend/UI/events/tests in structured sections.
Docstring Coverage ✅ Passed Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/primitive-execution

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/inspector/src/dashboard/react/utils/executePrimitive.ts (2)

195-256: response.json() cast to BackendResponse is unvalidated.

Line 220 casts the parsed JSON directly to BackendResponse without any runtime validation. If the response shape is unexpected (e.g., body.duration_ms is undefined), the value flows unchecked into _meta.duration_ms. This is low-risk since the error path at Line 225-232 uses nullish coalescing, but body.duration_ms on Line 230/250 could be undefined and silently assigned.

Consider at minimum a guard like typeof body.duration_ms === 'number' before assigning, or validate with a lightweight schema.


128-165: Prompt role mapping silently coerces "system" to "user".

Line 138: any role that isn't "assistant" is mapped to "user", including "system". This is likely intentional per MCP's prompt message spec (user/assistant only), but worth a brief inline comment to clarify the design choice for future maintainers.

packages/inspector/src/dashboard/react/styles.ts (1)

834-846: Consider extracting shared badge properties to reduce duplication.

eventBadgeManual duplicates six properties already defined in eventBadge (fontSize, padding, borderRadius, textTransform, fontWeight, letterSpacing, flexShrink). Since the manual badge is applied standalone (not spread with eventBadge), this duplication is functionally necessary, but you could DRY it up with a shared base constant.

This is minor and consistent with existing patterns, so fine to defer.

packages/inspector/src/connection.ts (1)

1119-1141: Update JSDoc to document the new source parameter.

The method's JSDoc block (lines 1120-1128) still lists only three @param entries. The new source parameter should be documented for API completeness.

📝 Proposed JSDoc update
    * `@param` type - Event type (agent-tool-call or agent-tool-result)
    * `@param` payload - Event payload (tool name, args, result, etc.)
    * `@param` protocol - Protocol used (mcp or openai)
+   * `@param` source - Event source (defaults to "agent" if omitted)
    */
packages/inspector/tests/execute-primitive-endpoint.test.ts (1)

33-42: Consider extracting the repeated fetch mock setup/teardown.

The save-restore pattern for globalThis.fetch is duplicated across four describe blocks. A shared helper or a top-level beforeEach/afterEach would reduce duplication.

♻️ Example: hoist to file-level
+const originalFetch = globalThis.fetch;
+
+beforeEach(() => {
+  vi.restoreAllMocks();
+});
+
+afterEach(() => {
+  globalThis.fetch = originalFetch;
+});
+
 describe("createExecuteFn", () => {
-  const originalFetch = globalThis.fetch;
-
-  beforeEach(() => {
-    vi.restoreAllMocks();
-  });
-
-  afterEach(() => {
-    globalThis.fetch = originalFetch;
-  });
-
   it("curries connectionId and baseUrl into the execute function", async () => {

(Apply the same removal to the other three describe blocks.)

Also applies to: 122-131, 328-337, 625-634

packages/inspector/src/standalone-server.ts (1)

630-642: The eventType guard on Line 638 is unreachable.

kind is already validated to be "tool" | "resource" | "prompt" at Line 536, so the eventTypes[kind] lookup will always succeed. The guard is harmless defensive code, but worth noting.

packages/inspector/src/types/inspector-event-types.ts (1)

65-72: Inconsistent naming convention: underscores vs. hyphens.

Existing agent event types use hyphens (agent-tool-call, agent-tool-result, agent-initialize), but the new manual event types use underscores (manual_tool_call, manual_tool_result, etc.). This creates an inconsistency in the InspectorEventType union.

If this was intentional (e.g., to visually distinguish manual events or align with a different subsystem convention), no action needed. Otherwise, consider aligning to hyphens for consistency.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: Primitive Execution & Always-Visible Event Logs

This PR implements manual execution of MCP primitives (tools/resources/prompts) from the inspector dashboard. Overall, the implementation is well-structured with excellent test coverage. However, there are several areas that need attention before merging.


🔴 Critical Issues

1. Timeout Memory Leak in Backend Endpoint

Location: packages/inspector/src/standalone-server.ts:~650-720 (execute-primitive endpoint)

The timeout implementation using Promise.race has a critical issue. Even though commit f9f6f7f attempted to fix timer leaks with try/finally, the timeout timer will not be cleared if the Promise.race rejects (e.g., on connection error or MCP call failure).

Fix: Wrap in try/catch/finally or ensure clearTimeout runs in all code paths

2. Type Safety Violation with 'as' Assertions

Location: packages/inspector/src/dashboard/react/utils/executePrimitive.ts:79, 115, 168-170

Multiple uses of 'as ExecutionMeta' bypass TypeScript safety. Per AGENTS.md: NEVER introduce any in production code; use unknown + narrowing


⚠️ Security Concerns

3. No Input Validation on Backend Endpoint

Location: packages/inspector/src/standalone-server.ts:521-571

The /api/execute-primitive endpoint accepts arbitrary params without validation. This creates risks for:

  • Prototype pollution if params contains proto
  • Unexpected behavior in MCP server
  • Potential injection vulnerabilities depending on MCP server implementation

Recommendation: Use Zod v4 schema validation (per repo standards) to validate params structure before execution.

4. CORS Configuration Too Permissive

Location: packages/inspector/src/standalone-server.ts:503-505

Access-Control-Allow-Origin: * allows ANY origin to execute primitives on connected MCP servers. For a local inspector, malicious websites could trigger tool executions.

Recommendation: Restrict to specific origins (localhost + configured) or require authentication/CSRF tokens


🟡 Code Quality Issues

5. Missing Error Context in Frontend

Location: packages/inspector/src/dashboard/react/utils/executePrimitive.ts:213-232

Error messages lack context about which primitive failed. Users won't know what went wrong during debugging.

6. Redundant Ternary in executePrimitive.ts

Location: Line 85

structuredContent: structuredContent !== undefined ? structuredContent : undefined is a no-op

7. Non-Exhaustive Switch Statement

Location: packages/inspector/src/dashboard/react/utils/executePrimitive.ts:236-246

No default case - result may be undefined for unexpected kinds


🔵 Performance Considerations

8. Unnecessary useMemo Recalculation

Location: packages/inspector/src/dashboard/react/InspectorDashboard.tsx:571-574

useMemo recalculates whenever selectedPrimitive changes (entire object). Consider memoizing on selectedPrimitive?.serverId instead.

9. Backend: Sequential Event Recording

Manual events are recorded synchronously. For high-frequency calls, consider batching or async event recording.


✅ Positive Observations

  1. Excellent Test Coverage: 105 new tests covering mappers, endpoint contracts, and event types
  2. Clean Architecture: Clear separation between frontend mappers and backend execution
  3. Type Safety: Proper use of type guards (isObj, getProp) for runtime validation
  4. Comprehensive Error Handling: Most error paths are covered
  5. Backward Compatibility: Optional onExecute prop preserves existing functionality
  6. Documentation: Good inline comments explaining design decisions

📋 Recommendations

Before Merge (Required)

  • Fix timeout memory leak (issue 1)
  • Add input validation with Zod schemas (issue 3)
  • Improve error messages with primitive context (issue 5)
  • Add default case to switch statement (issue 7)
  • Run full test suite: pnpm build && pnpm test && pnpm lint && pnpm typecheck (per AGENTS.md)

Nice to Have (Consider)

  • Replace type assertions with proper narrowing (issue 2)
  • Tighten CORS policy (issue 4)
  • Optimize useMemo dependencies (issue 8)
  • Clean up redundant ternary (issue 6)

Test Plan Verification

Based on PR description:

  • ✅ AC-01-08: Implementation looks complete
  • ⚠️ AC-09: Timeout logic has memory leak
  • ✅ AC-10: Manual events properly typed and recorded
  • ❓ Test plan not verified (tests require approval to run)

Next Steps: Address critical issues, run test suite, and confirm all 1621 tests pass.


Overall Assessment: Needs Changes 🟡

The implementation is solid but has critical bugs (timeout leak) and security concerns (input validation, CORS) that must be addressed before merging.

…SDoc)

- Remove no-op ternary for structuredContent in mapToolResponse
- Add exhaustive default case to kind switch in executePrimitive
- Add @PARAM JSDoc for source parameter in recordAgentEvent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude

claude Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Code Review: feat(inspector): primitive execution & always-visible event logs

Overview

This PR implements manual (browse-mode) primitive execution from the inspector dashboard and makes event/agent logs always visible. The implementation is well-structured with 105 new tests covering the core functionality.


Strengths

1. Code Quality & Architecture

  • Clean separation of concerns: Frontend mapper functions (mapToolResponse, mapResourceResponse, mapPromptResponse) handle response transformation independently with proper type safety
  • Defensive programming: All mapper functions never throw and safely handle malformed data using helper functions like getProp, isObj
  • Type safety: Comprehensive TypeScript types for ExecutionResult, ContentBlock, ResourceContent, and PromptMessage
  • Consistent error handling: Backend endpoint returns uniform response shape with ok, kind, data, error, and duration_ms

2. Backend Implementation (standalone-server.ts)

  • Proper timeout handling: Configurable 30s default timeout with Promise.race and cleanup
  • Connection registry support: Correctly resolves ConnectionManager by connectionId with fallback to active connection
  • Event recording: Manual events are properly tagged with source: manual for visual distinction in Agent panel
  • Validation: Request body is validated for required fields (kind, name) before execution

3. Frontend Implementation (executePrimitive.ts)

  • Network error handling: Catches fetch errors and provides user-friendly messages
  • JSON parsing safety: Catches parse errors and returns proper error result
  • Duration tracking: Backend-provided duration_ms is properly attached to results
  • Curried execution function: createExecuteFn provides clean API for component usage

4. Test Coverage

  • Comprehensive test suite: 105 new tests across 3 test files
  • Per-kind mapper testing: All response mappers have dedicated unit tests
  • Endpoint contract testing: Backend /api/execute-primitive endpoint thoroughly tested
  • Manual event type tests: All 6 new event types covered

5. UI/UX Improvements

  • Always-visible tabs: RightPanel now shows all 3 tabs (Agent/Events/Logs) regardless of streaming state - good UX improvement
  • Action mode: Browse mode cleanly transitions to action mode with form inputs
  • Loading states: Proper loading spinner and disabled states during execution
  • Response panel: Clear success/error indication with duration display

⚠️ Issues & Concerns

1. Security

Medium Priority:

  • Timeout cleanup race condition (standalone-server.ts:675-688): The current implementation might leak timers if an error occurs. The code already uses a finally block (lines 684-687), so this is handled correctly.

Low Priority:

  • CORS wildcard (standalone-server.ts:503-505): Using Access-Control-Allow-Origin: * is acceptable for a development tool but should be documented as a security consideration for production deployments.

2. Potential Bugs

Medium Priority:

  • Empty content arrays (executePrimitive.ts:45-46): mapToolResponse returns { ok: true, content: [], _meta: {} } for invalid data. This might mask actual errors. Consider returning ok: false when data is unexpectedly malformed.

Low Priority:

  • String coercion (executePrimitive.ts:152-160): In mapPromptResponse, content fallback logic coerces all non-string/array values. This might produce unexpected results for edge cases (though unlikely in MCP responses).

3. Performance

Low Priority:

  • No request cancellation: If a user triggers multiple executions rapidly, there's no mechanism to cancel in-flight requests. Consider using AbortController to cancel previous requests when a new one starts.

4. Code Consistency

Low Priority:

  • Inconsistent event type naming: Manual events use snake_case (manual_tool_call) while agent events use kebab-case (agent-tool-call). This is intentional per the code but worth noting for consistency.

  • Duplicated validation logic (standalone-server.ts:72-109): The VALID_INSPECTOR_EVENT_TYPES set duplicates the type definition. Consider generating this set from the type definition programmatically to avoid drift.


📋 Recommendations

High Priority

  1. Consider adding request cancellation with AbortController for better UX

Medium Priority

  1. Improve error visibility: Return ok: false when mapper receives invalid data shape
  2. Add error context: Include stack traces in error responses when in debug mode

Low Priority

  1. Consolidate event type validation
  2. Document security considerations for CORS wildcard
  3. Add integration tests covering full flow from UI to MCP server

📊 Test Coverage Assessment

Overall: Excellent

The PR includes 105 new tests across 3 files covering all critical paths.


🎯 Acceptance Criteria Review

All 10 acceptance criteria are met ✅


Summary

This is a well-crafted PR with solid architecture, comprehensive tests, and clean separation of concerns.

Overall assessment: ✅ Approve with minor improvements

The implementation follows repository conventions, adheres to strict TypeScript mode, and includes comprehensive tests meeting the 50% coverage threshold.

Great work! 🚀

@gabrypavanello
gabrypavanello merged commit 446b9a9 into main Feb 9, 2026
3 checks passed
@gabrypavanello
gabrypavanello deleted the feat/primitive-execution branch February 9, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants