Skip to content

[FEATURE] Define MCP tools and resources for Testownik #235

Description

@Antoni-Czaplicki

Context

Part of the MCP Server Integration initiative (Solvro/pm-testownik#42). Depends on #233 (base MCP server setup). OAuth (#234) can be developed in parallel but is needed before production deployment.

Motivation

The MCP server is only useful if it exposes meaningful tools. This issue covers defining the actual MCP tools that let AI agents interact with Testownik — searching quizzes, reading questions, managing folders, checking grades, and actively studying (getting questions, submitting answers).

Well-designed tools let AI assistants help students study more effectively by querying their quiz data, explaining questions in context, tracking their progress, and even conducting interactive study sessions.

Tools to implement (subject to change, more like a suggestion)

Quiz tools (scope: quizzes:read, quizzes:write)

Tool Description Auth
list_my_quizzes List quizzes owned by the current user read
search_quizzes Search accessible quizzes by name/description, with filters (visibility, subject) read
get_quiz Get full quiz details including metadata and statistics read
get_quiz_questions List questions in a quiz with answers read
create_quiz Create a new quiz with name, description, visibility write
add_question Add a question to a quiz (with answers, hints, image refs) write
edit_question Update question text, answers, or hints write
delete_question Remove a question from a quiz write

Study session tools (scope: sessions:write)

These tools enable AI agents to conduct interactive study sessions with the user — fetching questions, submitting answers, and tracking progress in real time.

Tool Description
get_quiz_session Get or create an active session for a quiz (returns session state, progress, reoccurrences)
reset_quiz_session Archive the current session and start a fresh one
get_next_question Get the next question to study based on the session's reoccurrence algorithm
submit_answer Submit an answer for a question in the current session. Accepts quiz_id, question_id, and selected_answers (answer IDs for closed questions, boolean for true/false, text string for open questions). Returns whether the answer was correct and the updated session state.
get_random_question Get a random question from the user's recently active quizzes (last 90 days)

The submit_answer tool wraps the existing record_answer endpoint logic:

  • Supports all question types: closed (multiple choice), true/false, and open (text)
  • Validates selected answers against the question
  • Records the AnswerRecord and updates session progress
  • Returns was_correct, correct answer details, and updated reoccurrence count

User tools (scope: user:read)

Tool Description
get_my_profile Return current user's profile info
get_my_settings Return user's quiz settings (reoccurrences, AI prefs, notifications)

Grade/progress tools (scope: grades:read)

Tool Description
get_quiz_progress Get user's progress on a specific quiz (sessions, scores)
get_quiz_statistics Get aggregated stats for a quiz (per-question breakdown)

Folder tools (scope: quizzes:read, quizzes:write)

Tool Description Auth
list_folders List user's quiz folders read
get_folder_quizzes Get quizzes in a folder read
add_quiz_to_folder Add a quiz to a folder write

Feedback tools (scope: feedback:write)

Tool Description
report_question_issue Report a bug/issue with a specific question

Implementation approach

Recommended: Leverage existing DRF views via drf_publish_* decorators

django-mcp-server has first-class DRF integration — we should use it heavily since we already have DRF views, serializers, and permissions for all of these operations. This avoids duplicating logic and keeps MCP tools in sync with the REST API automatically.

Publish existing DRF views as MCP tools:

from mcp_server import drf_publish_list_mcp_tool, drf_publish_create_mcp_tool

# Expose quiz list as an MCP tool — reuses QuizSerializer, permissions, filtering
@drf_publish_list_mcp_tool(instructions="List quizzes accessible to the current user")
class QuizListView(ListAPIView):
    serializer_class = QuizSerializer
    ...

Serialize tool output with existing serializers:

from mcp_server import MCPToolset, drf_serialize_output
from quizzes.serializers import QuizDetailSerializer

class QuizTools(MCPToolset):
    @drf_serialize_output(QuizDetailSerializer)
    def get_quiz(self, quiz_id: int):
        """Get full details for a specific quiz."""
        quiz = Quiz.objects.get(pk=quiz_id)
        ...
        return quiz

For study session tools, use MCPToolset wrapping existing view logic:

from mcp_server import MCPToolset

class StudyTools(MCPToolset):
    def submit_answer(self, quiz_id: str, question_id: str, selected_answers: list) -> dict:
        """Submit an answer for a question in the current study session.
        
        For closed questions: pass a list of answer UUIDs.
        For true/false questions: pass [true] or [false].
        For open questions: pass ["your answer text"].
        
        Returns whether the answer was correct and updated session progress."""
        # Reuse logic from QuizViewSet.record_answer
        ...

For read-only model access, use ModelQueryToolset:

from mcp_server import ModelQueryToolset
from quizzes.models import Quiz

class QuizQueryTool(ModelQueryToolset):
    model = Quiz

    def get_queryset(self):
        return super().get_queryset().filter(
            accessible_quizzes_q(self.request.user)
        )

Key principles

  • DRF-first — prefer drf_publish_* decorators and @drf_serialize_output over writing raw tool methods; this reuses existing serializers, permissions, and filtering logic
  • Don't duplicate — if a DRF view already does what the tool needs, publish it directly rather than reimplementing
  • Respect permissionsdjango-mcp-server disables built-in DRF auth/permissions/pagination on published views (it uses MCP auth instead), so ensure permission checks are explicit in tool logic or querysets
  • Scope-aware — tools should check OAuth scopes when auth is via MCP OAuth (from [FEATURE] Wire OAuth 2.0 authorization into MCP endpoint #236)
  • Good descriptions — MCP tool descriptions are what AI agents use to decide which tool to call; make them clear and specific

Note on DRF integration behavior

When using drf_publish_* decorators, django-mcp-server disables the following DRF features on the published view:

  • authentication_classes (MCP auth is used instead)
  • permission_classes
  • filter_backends
  • pagination_class (self.paginator will be None)

This means you need to handle filtering and access control in the queryset or tool logic directly, not rely on DRF's built-in permission classes.

Testing

  • Run python manage.py mcp_inspect to verify all tools are correctly declared
  • Test each tool with MCP Inspector
  • Test a full study flow: get session → get question → submit answer → check progress
  • Verify permission boundaries (user A can't access user B's private quizzes via MCP)
  • Test with an actual MCP client (e.g. Claude Desktop) to verify the AI agent experience
  • Ensure guest users get appropriate limited access

References

Metadata

Metadata

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions