> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pawtograder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server

> Architecture, auth model, and extension points for the Pawtograder MCP server

# MCP Server

Pawtograder exposes a Model Context Protocol (MCP) server as a Supabase
Edge Function. This page covers the implementation: how authentication
works, what the RLS guarantees are, what tools are available, and how to
extend the server.

For instructor-facing setup (connecting Claude Desktop, creating API
tokens, using the AI Help button), see
[AI assistance for helping students](/staff/ai-assistance).

## Architecture

The MCP server is a Supabase Edge Function that speaks streamable HTTP
MCP. In production it is reachable at:

```
<SUPABASE_URL>/functions/v1/mcp-server
```

When running Supabase locally, the function is served under the local
Edge Function URL printed by `npx supabase start` (the Supabase CLI also
prints a convenience MCP URL at `http://127.0.0.1:54321/mcp`).

The function shares types and helpers with the rest of Pawtograder's
edge functions under `supabase/functions/_shared/`. Database queries go
through the standard typed Supabase server client, so every query
respects the same Postgres row-level security policies the rest of the
app uses.

## Authentication

The server accepts long-lived JWTs issued by Pawtograder's API token
system (Settings → API Tokens) in the `Authorization: Bearer <token>`
header. Token properties:

* Issued and signed by Pawtograder; carry the issuing user's identity.
* Scoped to MCP read access (`mcp:read`).
* Revocable from the UI; revocation is checked on every request.
* Tracked with a `last_used_at` timestamp for audit.

A request that arrives without a valid, non-revoked token is rejected
with `401`.

## Authorization and RLS

Authorization happens in two layers:

1. **Role gate** – Before any tool runs, the server checks that the
   authenticated user has `instructor` or `grader` role in the
   class targeted by the request. Students cannot use the MCP server,
   even with a valid token.
2. **Postgres RLS** – Every database query runs as the authenticated
   user, so the same RLS policies that govern the web app also govern
   MCP responses. There is no service-role escalation inside tool
   handlers.

The server is designed to never leak data that the same user couldn't
see in the web app:

* The `users` table is never queried directly from tool handlers.
* The `is_private_profile` flag is not exposed in any tool response.
* All access goes through views and tables already guarded by RLS.

## Available tools

The full tool list is defined in the edge function source. At time of
writing it covers:

**Context**

* `get_help_request` — help request plus full chat history
* `get_discussion_thread` — thread plus replies
* `get_assignment` — assignment spec, `handout_url`, and rubric

**Submissions**

* `get_submission`, `list_submission_files`, `get_submission_files`
* `list_submission_tests`, `get_test_output`, `get_submission_build_output`

**Repositories**

* `list_grader_files`, `get_grader_files`
* `list_handout_files`, `get_handout_files`

**Search**

* `search_help_requests`, `search_discussion_threads`
* `get_submissions_for_student`

The authoritative list lives next to the handler implementations; treat
this section as a quick reference, not a contract.

## Assignment handout context

Assignments carry an optional `handout_url` column that points to the
spec students see. Several tools (notably `get_assignment` and the help
request / thread context tools) include `handout_url` in their response
so that an AI assistant can fetch the assignment description without
needing additional credentials.

## Extension points

Common reasons to extend the MCP server:

* **Adding a tool** — Define the tool descriptor (name, parameters,
  description) and implement the handler. Read data through the
  authenticated Supabase client so RLS is enforced automatically. Add
  the tool to the registry so it shows up in `tools/list`.
* **Expanding context** — When you add fields to existing tools, prefer
  composing existing queries over loosening RLS. If a new field requires
  privileged access, surface it through a dedicated view with explicit
  RLS rather than service-role queries inside the handler.
* **New auth scopes** — The API token system supports multiple scopes.
  If a tool needs to mutate state (e.g. posting a draft response), add a
  new scope rather than overloading `mcp:read`.

## Troubleshooting

### `401 Unauthorized`

* Token revoked, expired, or malformed.
* Missing `Authorization: Bearer` header.

### `403 Forbidden`

* The user is authenticated but not an instructor or grader in the
  class the request targets.

### `404` from a tool with no data

* The query succeeded but RLS filtered the result to empty. Verify the
  user really has access in the web app.

### Local server not reachable

* Confirm Supabase is running: `npx supabase status`.
* Confirm the edge function is being served (the Supabase CLI runs
  edge functions automatically when you start it; if you are using
  `npx supabase functions serve` manually, make sure `mcp-server` is
  in the served set).

## Contributing

The MCP server lives under `supabase/functions/mcp-server/` in the
[platform repo](https://github.com/pawtograder/platform). When changing
tool signatures, regenerate any shared types (`npm run client-local`)
and update the staff-facing docs in
[`staff/ai-assistance.mdx`](/staff/ai-assistance) if the tool list or
setup flow changes.
