Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a9b8c9d
Add Pawtograder MCP server for AI-assisted student help
claude Jan 29, 2026
9a84985
Enhance MCP server with automatic submission fetching and files
claude Jan 29, 2026
2273621
Implement JWT-based authentication for MCP server
claude Jan 30, 2026
d908221
Add self-serve MCP token management UI
claude Jan 30, 2026
ec06ef0
Remove standalone MCP server package
claude Jan 30, 2026
54661d5
Fix lint errors and TypeScript issues in MCP implementation
claude Jan 30, 2026
30b7baf
Fix CodeQL polynomial regex warning in base64UrlEncode
claude Jan 30, 2026
b9b6214
Replace console.error with Sentry in MCP server
claude Jan 30, 2026
5d142c0
Fix N+1 query patterns and add pagination in MCP server
claude Jan 30, 2026
5c22c91
Add TA feedback collection for AI assistance
claude Jan 30, 2026
a2669ea
Add AI help button to test insights for error pattern analysis
claude Jan 31, 2026
d00afde
Improve test insights AI prompt for assignment issues and discussion …
claude Jan 31, 2026
68b8e7d
Migrate API routes to Supabase Edge Functions
claude Jan 31, 2026
7b7cdee
Convert AI help feedback from Edge Function to Postgres RPC
claude Jan 31, 2026
7f25db7
Address PR feedback: security, code quality, and UX fixes
claude Jan 31, 2026
a5c7fa5
regen types
jon-bell Jan 31, 2026
30b0977
Address remaining PR feedback
jon-bell Jan 31, 2026
a269d95
Add aria-label for accessibility on icon-only AI analyze button
jon-bell Jan 31, 2026
39fc3a4
Address remaining PR feedback: security and robustness improvements
jon-bell Jan 31, 2026
4d64595
Fix TypeScript errors in MCPAuth.ts
jon-bell Jan 31, 2026
df89c0a
Fix TypeScript error in ErrorPinModal.tsx
jon-bell Jan 31, 2026
11dd5ce
Add AI assist button for test failures and build errors
jon-bell Jan 31, 2026
c438c87
Add MCP server and tokens Edge Functions to config
jon-bell Jan 31, 2026
3781199
Add global feedback widget to all AI help buttons
jon-bell Jan 31, 2026
616f01b
Add MCP setup dialog for users without tokens
claude Jan 31, 2026
292a5e3
Fix auth
jon-bell Feb 1, 2026
f33f079
Merge branch 'claude/pawtograder-mcp-server-J4xbx' of github.com:pawt…
jon-bell Feb 1, 2026
b6bf117
lint
jon-bell Feb 7, 2026
eb0ed8c
nits
jon-bell Feb 9, 2026
5514a8f
Remove unused duplicate data.ts from mcp-server
jon-bell Feb 11, 2026
345b083
nits
jon-bell Feb 11, 2026
a49b56f
lint
jon-bell Feb 11, 2026
1f17584
.
jon-bell Feb 11, 2026
31ed26b
Merge branch 'staging' into claude/pawtograder-mcp-server-J4xbx
jon-bell Feb 11, 2026
fc009ae
.
jon-bell Feb 11, 2026
3e4cf41
Enhance AI help prompts with evidence-based diagnosis and tone options
claude Feb 14, 2026
563ed8a
Fix newly failed E2E test?
jon-bell Feb 17, 2026
74614f0
.
jon-bell Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/course/[course_id]/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { RiChatSettingsFill } from "react-icons/ri";
import { TbSpy, TbSpyOff } from "react-icons/tb";
import { signOutAction } from "../../actions";
import { LuCopy, LuCheck } from "react-icons/lu";
import MCPTokensMenu from "@/components/settings/MCPTokensMenu";

function SupportMenu() {
// Track whether the build number has been successfully copied
Expand Down Expand Up @@ -699,6 +700,7 @@ function UserSettingsMenu() {
</HStack>
<ProfileChangesMenu />
<NotificationPreferencesMenu />
<MCPTokensMenu />
<Button
variant="ghost"
onClick={signOutAction}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { createClient } from "@/utils/supabase/client";
import { useClassProfiles } from "@/hooks/useClassProfiles";
import { useErrorPinMatches } from "@/hooks/useErrorPinMatches";
import { ErrorPinCallout } from "@/components/discussion/ErrorPinCallout";
import { AIHelpSubmissionErrorButton } from "@/components/ai-help/AIHelpSubmissionErrorButton";

function LLMHintButton({ testId, onHintGenerated }: { testId: number; onHintGenerated: (hint: string) => void }) {
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -716,9 +717,17 @@ function PyretRepl({
}

function GenericBuildError({
errorPinMatches
errorPinMatches,
buildOutput,
assignmentId,
classId,
submissionId
}: {
errorPinMatches?: Map<number | null, import("@/hooks/useErrorPinMatches").ErrorPinMatch[]>;
buildOutput?: string;
assignmentId: number;
classId: number;
submissionId: number;
}) {
// Get all matches (both submission-level and test-level) for build errors
const allMatches: import("@/hooks/useErrorPinMatches").ErrorPinMatch[] = [];
Expand All @@ -735,9 +744,20 @@ function GenericBuildError({
return (
<Box mt={3}>
<Box p={3} bg="bg.error" borderRadius="md" border="1px solid" borderColor="border.error">
<Text fontWeight="bold" color="fg.error" fontSize="sm">
Error: Gradle build failed
</Text>
<HStack justify="space-between">
<Text fontWeight="bold" color="fg.error" fontSize="sm">
Error: Gradle build failed
</Text>
{buildOutput && (
<AIHelpSubmissionErrorButton
errorType="build_error"
errorOutput={buildOutput}
assignmentId={assignmentId}
classId={classId}
submissionId={submissionId}
/>
)}
</HStack>
<Box mt={2} p={2} bg="bg.error" borderRadius="sm">
<Text color="fg.error">
The autograding script failed to build your code. Please inspect the output below for more details:
Expand Down Expand Up @@ -952,9 +972,19 @@ export default function GraderResults() {
);
const hasBuildError = query.data.data.grader_results.lint_output === "Gradle build failed";
const data = query.data.data;
// Get build output for AI analysis
const buildOutput = data.grader_results?.grader_result_output?.[0]?.output || data.grader_results?.lint_output || "";
return (
<Box>
{hasBuildError && <GenericBuildError errorPinMatches={errorPinMatches} />}
{hasBuildError && (
<GenericBuildError
errorPinMatches={errorPinMatches}
buildOutput={buildOutput}
assignmentId={data.assignment_id}
classId={data.class_id}
submissionId={data.id}
/>
)}
<Tabs.Root
m={3}
defaultValue={hasBuildError ? data.grader_results?.grader_result_output[0]?.visibility : "tests"}
Expand Down Expand Up @@ -1088,15 +1118,31 @@ export default function GraderResults() {
};
const style = result.max_score === 0 ? "info" : result.score === result.max_score ? "success" : "error";
const showScore = result.max_score !== 0;
const isFailing = (result.max_score ?? 0) > 0 && (result.score ?? 0) < (result.max_score ?? 0);

const testMatches = errorPinMatches.get(result.id) || [];

return (
<CardRoot key={result.id} id={`test-${result.id}`} mt={4}>
<CardHeader bg={`bg.${style}`} p={2}>
<Heading size="lg" color={`fg.${style}`}>
{result.name} {showScore ? result.score + "/" + result.max_score : ""}
</Heading>
<HStack justify="space-between">
<Heading size="lg" color={`fg.${style}`}>
{result.name} {showScore ? result.score + "/" + result.max_score : ""}
</Heading>
{isFailing && result.output && (
<AIHelpSubmissionErrorButton
errorType="test_failure"
testName={result.name}
testPart={result.part}
score={result.score ?? undefined}
maxScore={result.max_score ?? undefined}
errorOutput={result.output}
assignmentId={data.assignment_id}
classId={data.class_id}
submissionId={data.id}
/>
)}
</HStack>
</CardHeader>
{testMatches.length > 0 && (
<Box px={4} pt={2}>
Expand Down
10 changes: 10 additions & 0 deletions app/course/[course_id]/discussion/[root_id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { AIHelpIconButton } from "@/components/ai-help/AIHelpButton";
import DiscordDiscussionMessageLink from "@/components/discord/discord-discussion-message-link";
import { ErrorPinManageModal } from "@/components/discussion/ErrorPinManageModal";
import { KarmaBadge } from "@/components/discussion/KarmaBadge";
Expand Down Expand Up @@ -167,6 +168,15 @@ function ThreadActions({
</Tooltip>
{/* Discord link - shown if thread has a Discord message (staff only see it) */}
<DiscordDiscussionMessageLink threadId={thread.id} />
{/* AI Help button for staff - component has internal tooltip */}
{canPin && (
<AIHelpIconButton
contextType="discussion_thread"
resourceId={thread.id}
classId={thread.class_id}
assignmentId={topicAssignmentId ?? undefined}
/>
)}
{/* <Tooltip content="Emote">
<Button aria-label="Emote" variant="ghost" size="sm">
<FaSmile />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export default function TestInsightsPage() {
onCreateErrorPin={handleCreateErrorPin}
onViewSubmissions={handleViewSubmissions}
onRegradeSubmissions={handleRegradeSubmissions}
assignmentId={validAssignmentId}
classId={validCourseId}
/>
</Box>
</Tabs.Content>
Expand Down
21 changes: 16 additions & 5 deletions app/course/[course_id]/manage/assignments/new/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -742,14 +742,25 @@ export default function AssignmentForm({
</Checkbox.Root>
</Field>
</Fieldset.Content>
{/* <Fieldset.Content>
<Fieldset.Content>
<Field
label="Description URL"
helperText="A link to the description of the assignment, e.g. on a course website or in Canvas"
label="Handout URL"
helperText="A link to the assignment handout or instructions document. This URL will be provided to AI assistants helping students with this assignment."
errorText={errors.handout_url?.message?.toString()}
invalid={!!errors.handout_url}
>
<Input name="description" />
<Input
type="url"
placeholder="https://..."
{...register("handout_url", {
pattern: {
value: /^https?:\/\/.+/,
message: "Please enter a valid URL starting with http:// or https://"
}
})}
/>
</Field>
</Fieldset.Content> */}
</Fieldset.Content>
<Fieldset.Content>
<Field
label="Points Possible"
Expand Down
Loading