Skip to content

feat(backend): expose Studio as Streamable HTTP MCP server#287

Merged
betterclever merged 5 commits intomainfrom
eng-191/studio-mcp-server
Feb 13, 2026
Merged

feat(backend): expose Studio as Streamable HTTP MCP server#287
betterclever merged 5 commits intomainfrom
eng-191/studio-mcp-server

Conversation

@betterclever
Copy link
Contributor

Summary

  • Expose Studio as a Streamable HTTP MCP server at /api/v1/studio-mcp for external AI agents
  • Register 9 tools: list_workflows, get_workflow, run_workflow, list_components, get_component, list_runs, get_run_status, get_run_result, cancel_run
  • Authenticate via API keys (Bearer sk_live_...) with permission-based access control
  • Add 12 unit tests for the MCP service and 3 AI SDK e2e tests (tool discovery, agent list_components, agent workflow execution)

Test plan

  • Unit tests: 12 pass, 29 assertions — service instantiation, tool registration, auth context, isolation
  • E2e tests: AI SDK createMCPClient connects over Streamable HTTP, discovers all 9 tools
  • E2e tests: AI agent (glm-4.7 via ZAI) calls list_components through MCP and reports results
  • E2e tests: AI agent calls run_workflowget_run_statusget_run_result sequence through MCP
  • tsc --build passes clean
  • Full test suite: 573 pass, 0 fail

Closes ENG-191

🤖 Generated with Claude Code

betterclever and others added 4 commits February 13, 2026 11:18
…al agents

Add studio-mcp module that enables any MCP client (Claude Code, Cursor, etc.)
to connect to Studio using API keys and interact via 9 tools:
list_workflows, get_workflow, run_workflow, list_components, get_component,
list_runs, get_run_status, get_run_result, cancel_run.

Uses StreamableHTTPServerTransport (new MCP standard), session management,
and org-scoped auth via existing API key infrastructure.

Refs: ENG-191

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…erver

Unit tests verify tool registration, auth context isolation, and all 9 tool
handlers. E2e test uses @ai-sdk/mcp createMCPClient + generateText with
stepCountIs for proper agent loop validation against the Streamable HTTP
MCP endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…erver

Unit tests verify tool registration, auth context isolation, and all 9 tool
handlers. E2e test uses @ai-sdk/mcp createMCPClient + generateText with
stepCountIs for proper agent loop validation against the Streamable HTTP
MCP endpoint. Uses z.ai coding API with glm-4.7 model.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Tool calls use `input` (not `args`) and tool results use `output`
for the dynamic MCP tool types in AI SDK v6.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 659fb0e1c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +115 to +118
const handle = await this.workflowsService.run(
args.workflowId,
{ inputs: args.inputs ?? {}, versionId: args.versionId },
auth,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce API-key permissions for MCP workflow/run tools

The MCP handlers invoke workflow and run operations directly without any API-key permission gate, so keys that intentionally disable actions (for example workflows.run or runs.cancel) can still execute them through this endpoint. This is a privilege-escalation path for least-privilege API keys because run_workflow (and similarly list_workflows, get_workflow, list_runs, get_run_status, get_run_result, cancel_run) only depends on authenticated context, not the key’s permission matrix.

Useful? React with 👍 / 👎.

Comment on lines 51 to 53
if (sessionId) {
const transport = this.transports.get(sessionId);
if (!transport) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind MCP session reuse to the same authenticated principal

Requests with an existing mcp-session-id are accepted based only on transport lookup, but each transport was initialized with the original caller’s auth context. That means any authenticated caller who obtains a live session ID can continue issuing tool calls under the creator’s organization/identity, creating cross-tenant/cross-user access if the session ID leaks via logs, proxies, or shared clients. Session metadata should include the creator identity and reject mismatched req.auth on reuse.

Useful? React with 👍 / 👎.

Address Codex P1 review findings:

1. Permission gating: All workflow/run MCP tools now check
   auth.apiKeyPermissions before executing. Keys with restricted
   permissions (e.g. workflows.run=false) are properly denied.
   Component tools remain ungated (static metadata).

2. Session identity binding: MCP sessions store the creator's
   userId + organizationId. Subsequent requests verify the caller
   matches the session creator, returning 403 on mismatch to
   prevent cross-tenant session hijacking.

3. Extended AuthContext with optional apiKeyPermissions field,
   populated during API key authentication in AuthGuard.

Tests: 26 pass (19 service + 7 controller), 61 assertions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
@betterclever betterclever merged commit 2ae2de4 into main Feb 13, 2026
3 checks passed
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.

1 participant