Skip to content

feat(api): implement ChatKit server with MCP integration#9

Merged
mjunaidca merged 2 commits into
mainfrom
006-chat-server
Dec 7, 2025
Merged

feat(api): implement ChatKit server with MCP integration#9
mjunaidca merged 2 commits into
mainfrom
006-chat-server

Conversation

@mjunaidca

Copy link
Copy Markdown
Owner

Summary

  • Implement ChatKit server with MCP integration for conversational task management
  • Add PostgreSQL-based conversation persistence (chatkit_store module)
  • Add /chatkit endpoint with JWT authentication for MCP tool calls
  • Pass access_token to agent system prompt for authenticated API access

Changes

New Files

  • packages/api/src/taskflow_api/chatkit_store/ - PostgreSQL store for ChatKit
  • packages/api/src/taskflow_api/services/chatkit_server.py - ChatKit server implementation
  • packages/api/src/taskflow_api/services/chat_agent.py - Agent configuration with MCP
  • specs/006-chat-server/ - Feature specification

Modified Files

  • packages/api/src/taskflow_api/main.py - Add /chatkit endpoint, extract JWT token
  • packages/api/src/taskflow_api/config.py - Environment variable handling
  • packages/api/pyproject.toml - Add openai-agents, openai-chatkit, python-dotenv

Authentication Flow

  1. Frontend sends Authorization: Bearer <jwt> and X-User-ID headers
  2. /chatkit endpoint extracts token and passes to ChatKit server via metadata
  3. Agent system prompt includes user_id and access_token
  4. Agent passes these credentials in every MCP tool call
  5. MCP server uses token to call TaskFlow API with proper auth

Test plan

  • Start API server with TASKFLOW_CHATKIT_DATABASE_URL configured
  • Send ChatKit request with Authorization header
  • Verify agent includes credentials in MCP tool calls
  • Verify MCP server can authenticate to TaskFlow API

🤖 Generated with Claude Code

Add conversational task management via ChatKit protocol:

- Add PostgreSQL-based ChatKit store (chatkit_store/) for conversation persistence
- Implement TaskFlowChatKitServer with MCP server integration
- Add /chatkit endpoint matching ChatKit protocol specification
- Extract JWT token from Authorization header for MCP tool calls
- Pass user_id and access_token to agent system prompt for auth
- Configure environment variables (TASKFLOW_CHATKIT_DATABASE_URL)
- Add python-dotenv for OPENAI_API_KEY loading

The agent discovers tools dynamically from MCP server and includes
auth credentials in every tool call for authenticated API access.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings December 7, 2025 15:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a ChatKit server with MCP (Model Context Protocol) integration for conversational task management in the TaskFlow API. The implementation adds natural language chat capabilities backed by OpenAI's Agents SDK, with conversation persistence using PostgreSQL and authentication via JWT tokens.

Key Changes

  • Added ChatKit server infrastructure with PostgreSQL-based conversation persistence (chatkit_store module)
  • Implemented agent configuration that connects to TaskFlow MCP Server for tool discovery and execution
  • Added /chatkit endpoint with JWT token extraction (but missing validation) for chat requests
  • Integrated OpenAI Agents SDK and ChatKit dependencies

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
specs/006-chat-server/spec.md Feature specification defining user scenarios, requirements, and success criteria for chat server
specs/006-chat-server/plan.md Detailed implementation plan with phases, architecture, and MCP integration approach
services/chat_agent.py Agent configuration with system prompt containing authentication credentials (security concern)
services/chatkit_server.py ChatKit server implementation that connects to MCP and streams agent responses
chatkit_store/postgres_store.py PostgreSQL store with user isolation, but potential SQL injection via schema name
chatkit_store/config.py Store configuration with database URL validation and environment variable handling
main.py Added /chatkit endpoint with JWT extraction but missing validation (critical security issue)
config.py Added MCP server URL and OpenAI API key settings (key marked optional when required)
pyproject.toml Added openai-agents, openai-chatkit, and python-dotenv dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +44
CRITICAL: When calling ANY MCP tool, you MUST ALWAYS include these parameters:
- user_id: "{user_id}"
- access_token: "{access_token}"

## User Context
- User Name: {user_name}
- Current Project: {project_name} (ID: {project_id})

## Conversation History
{history}

## Your Capabilities
Using the available MCP tools, you can:
- **Add tasks**: Create new tasks in the current project
- **List tasks**: Show all tasks, filter by status (pending, in_progress, completed)
- **Update tasks**: Modify task title, description, status, or assignment
- **Complete tasks**: Mark tasks as done
- **Delete tasks**: Remove tasks from the project
- **Assign tasks**: Assign tasks to team members or agents

## Guidelines
1. Always confirm actions with the user after executing them
2. When listing tasks, format them clearly with ID, title, status, and assignee
3. If a request is ambiguous, ask for clarification
4. If a task is not found, suggest listing tasks to find the correct one
5. Be concise and helpful
6. ALWAYS include user_id="{user_id}" and access_token="{access_token}" in every tool call

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

The system prompt instructs the agent to include user_id and access_token in every MCP tool call, but these parameters are not being extracted from the prompt formatting. The agent system has no mechanism to automatically inject these values into tool calls based on the instructions alone.

This needs to be handled either:

  1. At the MCP transport layer by intercepting tool calls and adding the credentials
  2. By the MCP server expecting them in the request metadata rather than as tool parameters
  3. By using a custom tool wrapper that automatically adds these parameters

The current implementation appears to rely on the AI understanding and following instructions to include these values, which is unreliable.

Copilot uses AI. Check for mistakes.
Comment on lines +239 to +242
def _get_table_name(self, table: str) -> str:
"""Get fully qualified table name with schema."""
schema = self.config.schema_name if self.config else "taskflow_chat"
return f"{schema}.{table}"

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

SQL injection vulnerability: The schema name from config is being directly interpolated into SQL queries using f-strings. While the default value is safe, if schema_name can be configured by users, this could allow SQL injection.

Use parameterized queries or validate that schema_name only contains alphanumeric characters and underscores.

Copilot uses AI. Check for mistakes.
mcp_server_url: str = "http://localhost:8001/mcp"

# OpenAI API Key (required for chat)
openai_api_key: str | None = None

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

Missing required openai_api_key validation. The field is marked as optional (str | None) with no default value, but the OpenAI Agents SDK requires this key to function. This will cause runtime errors when chat is enabled but the API key is not set.

Change to: openai_api_key: str (required) or add validation in chat_enabled property to check if the key is set.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +15
# Load .env before anything else (for OPENAI_API_KEY used by Agents SDK)
load_dotenv()

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

The load_dotenv() call at module level may not load environment variables early enough for pydantic-settings to pick them up, since Settings initialization happens during module import. Consider calling load_dotenv() before importing Settings, or document that environment variables must be set before starting the application.

Alternatively, move this to the very top of the file before any imports that might trigger Settings initialization.

Copilot uses AI. Check for mistakes.
Comment on lines +154 to +163
# Extract JWT token from Authorization header (Bearer token)
auth_header = request.headers.get("Authorization")
access_token = None
if auth_header and auth_header.startswith("Bearer "):
access_token = auth_header[7:] # Remove "Bearer " prefix

if not access_token:
raise HTTPException(
status_code=401, detail="Missing Authorization header with Bearer token"
)

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

Missing authentication enforcement on the /chatkit endpoint. While the code extracts the JWT token and raises an error if missing, there's no actual JWT validation being performed. The token is extracted but never verified for authenticity, expiration, or proper claims.

This allows anyone to send any string as a Bearer token and access the chat functionality. The endpoint needs to validate the JWT using the same authentication mechanism as other API endpoints (e.g., using get_current_user dependency).

Copilot uses AI. Check for mistakes.
raise HTTPException(status_code=400, detail="Invalid JSON payload")
except Exception as e:
logger.exception("Error processing ChatKit request: %s", e)
raise HTTPException(status_code=500, detail=f"Error processing request: {e!s}")

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

Logging the full exception with detail=f"Error processing request: {e!s}" may expose sensitive information like database connection strings, internal paths, or stack traces to the client.

Use a generic error message for the client response and only log detailed errors server-side.

Suggested change
raise HTTPException(status_code=500, detail=f"Error processing request: {e!s}")
raise HTTPException(status_code=500, detail="Internal server error")

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +20
## Authentication Context
- User ID: {user_id}
- Access Token: {access_token}

CRITICAL: When calling ANY MCP tool, you MUST ALWAYS include these parameters:
- user_id: "{user_id}"
- access_token: "{access_token}"

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

The system prompt includes the access token in plain text with placeholders that will be formatted. This means the token will be visible in the prompt sent to the OpenAI API and potentially logged. Consider removing the access token from the prompt instructions and only pass it as metadata to tool calls.

The token is already being passed through the metadata in the RequestContext and can be accessed when making tool calls without exposing it in the prompt itself.

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +98
instructions = TASKFLOW_SYSTEM_PROMPT.format(
user_name=user_name,
project_name=project_name or "No project selected",
project_id=project_id or "N/A",
history=history or "No previous messages",
)

Copilot AI Dec 7, 2025

Copy link

Choose a reason for hiding this comment

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

Missing named argument for string format. Format ["You are TaskFlow Assistant, an AI helper for task management.

Authentication Context

  • User ID: {user_id}
  • Access Token: {access_token}

CRITICAL: When calling ANY MCP tool, you MUST ALWAYS include these parameters:

  • user_id: "{user_id}"
  • access_token: "{access_token}"

User Context

  • User Name: {user_name}
  • Current Project: {project_name} (ID: {project_id})

Conversation History

{history}

Your Capabilities

Using the available MCP tools, you can:

  • Add tasks: Create new tasks in the current project
  • List tasks: Show all tasks, filter by status (pending, in_progress, completed)
  • Update tasks: Modify task title, description, status, or assignment
  • Complete tasks: Mark tasks as done
  • Delete tasks: Remove tasks from the project
  • Assign tasks: Assign tasks to team members or agents

Guidelines

  1. Always confirm actions with the user after executing them
  2. When listing tasks, format them clearly with ID, title, status, and assignee
  3. If a request is ambiguous, ask for clarification
  4. If a task is not found, suggest listing tasks to find the correct one
  5. Be concise and helpful
  6. ALWAYS include user_id="{user_id}" and access_token="{access_token}" in every tool call

Response Format

  • For task lists, use a clear formatted list with key details
  • For confirmations, briefly state what was done and the result
  • For errors, explain what went wrong and suggest next steps
    "](1) requires 'user_id', but it is omitted.
    Missing named argument for string format. Format ["You are TaskFlow Assistant, an AI helper for task management.

Authentication Context

  • User ID: {user_id}
  • Access Token: {access_token}

CRITICAL: When calling ANY MCP tool, you MUST ALWAYS include these parameters:

  • user_id: "{user_id}"
  • access_token: "{access_token}"

User Context

  • User Name: {user_name}
  • Current Project: {project_name} (ID: {project_id})

Conversation History

{history}

Your Capabilities

Using the available MCP tools, you can:

  • Add tasks: Create new tasks in the current project
  • List tasks: Show all tasks, filter by status (pending, in_progress, completed)
  • Update tasks: Modify task title, description, status, or assignment
  • Complete tasks: Mark tasks as done
  • Delete tasks: Remove tasks from the project
  • Assign tasks: Assign tasks to team members or agents

Guidelines

  1. Always confirm actions with the user after executing them
  2. When listing tasks, format them clearly with ID, title, status, and assignee
  3. If a request is ambiguous, ask for clarification
  4. If a task is not found, suggest listing tasks to find the correct one
  5. Be concise and helpful
  6. ALWAYS include user_id="{user_id}" and access_token="{access_token}" in every tool call

Response Format

  • For task lists, use a clear formatted list with key details
  • For confirmations, briefly state what was done and the result
  • For errors, explain what went wrong and suggest next steps
    "](1) requires 'access_token', but it is omitted.

Copilot uses AI. Check for mistakes.
load_dotenv() must run before imports that read environment variables.
Added noqa: E402 to suppress linter warnings for these intentional imports.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mjunaidca mjunaidca merged commit 63db919 into main Dec 7, 2025
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.

2 participants