The AI Assistant You Didn't Ask For (But Your Users Will)
Here's the thing about shipping an MCP server: you build it, you document it, you move on. Then someone asks "can I chat with my data?" and suddenly you're looking at a six-month project.
Plane already has plane-mcp-server. It exposes issues, cycles, projects — everything via the Model Context Protocol. Claude Desktop can query it. Cursor can use it. But your users? They're in the browser. They don't care about MCP. They want a text box.
So here's the proposal: feature-flagged, disabled-by-default AI Assistant that talks to Plane through its own MCP server.
Not a rewrite. Not a new API. Same protocol, different client.
The Architecture
Browser UI → AI Assistant Component → Next.js API Route
↓
plane-mcp-server (localhost or container)
↓
Plane Database (PostgreSQL)
The MCP server already handles authentication, query construction, and data access. The AI Assistant is just a new consumer of that same interface.
// app/api/ai/chat/route.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["plane-mcp-server/dist/index.js"],
env: {
PLANE_API_KEY: process.env.PLANE_API_KEY,
PLANE_BASE_URL: process.env.PLANE_BASE_URL,
},
});
const client = new Client({
name: "plane-ai-assistant",
version: "1.0.0",
});
await client.connect(transport);
export async function POST(req: Request) {
const { query } = await req.json();
const result = await client.request({
method: "tools/call",
params: {
name: "plane_query",
arguments: { query },
},
});
return Response.json(result);
}
That's it. The MCP server becomes your AI backend. No separate vector store. No custom RAG pipeline. Just the protocol you already own.
Feature Flagging
This stays off by default. Why? Because AI costs money, and not everyone wants it.
// lib/features.ts
export const FEATURES = {
AI_ASSISTANT: process.env.NEXT_PUBLIC_ENABLE_AI_ASSISTANT === "true",
};
// components/Sidebar.tsx
import { FEATURES } from "@/lib/features";
export function Sidebar() {
return (
<aside>
<ProjectList />
{FEATURES.AI_ASSISTANT && <AIAssistant />}
</aside>
);
}
One env var. One toggle. Zero risk.
The UI
Three components. That's all.
// components/AIAssistant.tsx
"use client";
import { useState } from "react";
import { ChatInput } from "./ChatInput";
import { ChatMessages } from "./ChatMessages";
export function AIAssistant() {
const [messages, setMessages] = useState<
{ role: "user" | "assistant"; content: string }[]
>([]);
const [isLoading, setIsLoading] = useState(false);
const sendMessage = async (content: string) => {
setMessages((prev) => [...prev, { role: "user", content }]);
setIsLoading(true);
const res = await fetch("/api/ai/chat", {
method: "POST",
body: JSON.stringify({ query: content }),
});
const data = await res.json();
setMessages((prev) => [
...prev,
{ role: "assistant", content: data.content },
]);
setIsLoading(false);
};
return (
<div className="border rounded-lg p-4 space-y-4">
<ChatMessages messages={messages} isLoading={isLoading} />
<ChatInput onSend={sendMessage} disabled={isLoading} />
</div>
);
}
The MCP server handles the heavy lifting. Your UI is just a pipe.
What It Can Do Right Now
With plane-mcp-server exposing tools like list_issues, get_cycle, search_projects, the AI can answer:
- "Show me all high-priority bugs in the current sprint"
- "What's the status of issue PLANE-1234?"
- "Which projects have overdue tasks?"
No training. No fine-tuning. The MCP server translates natural language into structured queries against your existing data.
What You're Not Doing
- Not building a vector database. The MCP server queries Postgres directly.
- Not training a model. You're using GPT-4o or Claude via the MCP server.
- Not rewriting auth. The MCP server handles API keys and permissions.
- Not deploying new infrastructure. It runs in a container alongside Plane.
The Caveats
This isn't production-ready magic. Here's what breaks:
Latency. MCP over stdio means a subprocess per request. For production, you'd want the MCP server running as a persistent HTTP service.
Cost. Every query hits an LLM. Without caching, a busy team could burn through tokens fast. Add a simple cache:
const cache = new Map<string, string>();
export async function POST(req: Request) {
const { query } = await req.json();
if (cache.has(query)) {
return Response.json({ content: cache.get(query) });
}
// ... MCP call ...
cache.set(query, result.content);
return Response.json(result);
}
Security. The MCP server has access to everything. Make sure your AI assistant only queries, never mutates. Or add a confirmation dialog for destructive operations.
The Hook
You already built the hard part. plane-mcp-server is a fully functional AI backend sitting in your repo, unused by your own UI.
The AI Assistant proposal isn't about building something new. It's about connecting what you already have to where your users actually work — the browser.
Feature flag it. Ship it disabled. Let power users opt in.
Your MCP server is already talking to your data. It's time your UI started listening.
Debugging AI agents shouldn't feel like reading The Matrix.
Join other engineers who are building reliable autonomous workflows in our community: TracePilot Discord
Top comments (0)