fix(mcp): resolve {{LIBRECHAT_USER_ID}} in loadTools' MCP tool-loading path - #32
Closed
ForisKuang wants to merge 73 commits into
Closed
fix(mcp): resolve {{LIBRECHAT_USER_ID}} in loadTools' MCP tool-loading path#32ForisKuang wants to merge 73 commits into
ForisKuang wants to merge 73 commits into
Conversation
- Pre-create required runtime directories (/app/api/logs, /app/uploads, /app/client/public/images) - Set proper ownership to node user for /app directory - Run container as non-root user (USER node) Fixes EACCES permission denied errors when LibreChat tries to create the logs directory at runtime. Makes multi-stage build compatible with docker-compose user directive. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add dompurify (v3.2.6) dependency for HTML sanitization - Update Landing.tsx to render descriptions as sanitized HTML - Convert agent description input to textarea for multi-line support - Maintain 512 character limit with improved UX Changes enable rich HTML formatting in agent descriptions while ensuring security through DOMPurify sanitization. Source: danny-avila#8291 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Uses vite-plugin-pwa's selfDestroying option which generates a special service worker that unregisters itself and clears all caches. This fixes stale cache issues causing blank pages after deployments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the product feedback feature is enabled (via FEEDBACK_ISSUE_SERVICE_URL env var), clicking thumbs-down on an assistant message opens a feedback modal instead of the default tag popover. The modal collects a categorized issue report that is submitted to an external issue creation service, enabling automated GitHub issue creation from user feedback.
…sues Instead of proxying to an external service, the feedback route now calls the GitHub API directly to create issues. This eliminates the need for a separate middleware service. Configuration uses GITHUB_FEEDBACK_TOKEN and GITHUB_FEEDBACK_REPO env vars. Issues are formatted with structured markdown and labeled based on feedback reason.
…bmitted The existing feedback storage endpoint was not being called when submitting via the product feedback modal. Now handleFeedback is invoked on modal submit to store the thumbs-down rating in MongoDB alongside creating the GitHub issue.
…xt field Instead of creating a separate ProductFeedback MongoDB collection, serialize the entire feedback payload (reason, title, details, suggested fix, conversation context) as a JSON string in the existing message feedback `text` field. This avoids schema changes while preserving all feedback data.
…d add agent routing - Remove all GitHub API integration from feedbackIssues route; feedback is now stored in a new ProductFeedback MongoDB collection for manual review - Add endpoint, model, and agent_id to the feedback payload so feedback can be attributed to the specific agent it was submitted against - Thread agent identity from conversation through HoverButtons → Feedback → ProductFeedbackModal - Change feature flag from GITHUB_FEEDBACK_TOKEN/GITHUB_FEEDBACK_REPO to a simple PRODUCT_FEEDBACK_ENABLED boolean env var - Update response types from issue_url/issue_number to id/request_id - Simplify success toast to a plain confirmation (no GitHub link)
- Make thumbs up/down feedback buttons amber/yellow for better visibility - Remove edit and fork buttons from message hover actions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When agent responses use the content array format (with think/text blocks), msg.text is empty. Fall back to extracting text from msg.content to capture the actual response in feedback context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix thumbs-down bug where both thumbs stayed visible - Remove title field from thumbs-down modal, make categories optional/multi-select - Add dynamic placeholder text based on selected feedback category - Rename "Suggested Fix" to "Expected Outcome" - Add "Suggested System Prompt" field for power users - Add thumbs-up modal with category pills and optional comment - Green thumbs-up / red thumbs-down colors, move feedback to last position - Replace agent dropdown with horizontal button selector - Add powerUserEmails config and POWER_USER_EMAILS env var - Add GitHub Actions workflow for multi-arch Docker builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
K8s nodes are all arm64, no need for amd64 images. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The buttons were only updating local state without triggering the actual agent/conversation switch. Now uses the context's handleSelectSpec which calls onSelectSpec → newConversation. Also fix layout to inline-flex. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Agent buttons only show on new conversations, disappear once started - Use spec.name (e.g. "cBioDBAgent") instead of full label description Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use ubuntu-24.04-arm for native arm64 builds instead of QEMU emulation. Should cut build time from ~50min to ~10-15min. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables filtering by agent in Langfuse (e.g. cBioDBAgent vs cBioNavigator). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user submits thumbs up/down feedback, look up the corresponding Langfuse trace by messageId and create a score (1 for thumbsUp, 0 for thumbsDown) with the feedback comment. Fire-and-forget to avoid blocking the response. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, clicking thumbs down with productFeedbackEnabled would immediately save the feedback before the modal was submitted, causing stale state and the wrong modal to appear on re-click. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e and reason The frontend no longer sends feedback_title (removed field) and sends feedback_reason as optional comma-separated values. Updated backend route and Mongoose schema to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking an active thumbs-down button now clears the feedback (same as thumbs-up), instead of reopening the modal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of immediately clearing feedback when clicking the active thumb, show a summary dialog displaying the stored tags/comment/details with options to Delete (remove) or Update (re-open the appropriate modal). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace literal strings with i18n localize() calls and fix prettier formatting. Also fix pre-existing Cancel/Submit literals in thumbs-up modal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Local state was set to { rating: 'thumbsDown' } without the text field,
so the summary dialog had no data to display. Now both local state and
parent callback receive the full feedback object including the JSON text.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The feedback mutation was invalidating the wrong cache key ([QueryKeys.messages, messageId] instead of [QueryKeys.messages, conversationId]), so the messages cache was never updated after feedback was given. When users navigated away and returned, the cached messages had no feedback data, making the thumbs buttons appear unclicked. Fix: Use setQueryData to directly update the specific message's feedback field in the messages cache after a successful feedback mutation.
toMinimalFeedback() required a tag to be present, returning undefined
when no tag was set. This caused the new feedback modals (which store
data in the text field as JSON rather than using tags) to send
{ feedback: undefined } to the API, clearing the feedback instead of
saving it.
- Make tag optional in feedbackSchema and toMinimalFeedback
- Handle undefined tag in React Query cache update
fix: Persist thumbs up/down feedback state across navigation
…data Clicking an active feedback thumb now opens the editable submission modal (pre-filled with existing data) instead of the read-only summary dialog. Delete buttons inside the modals replace the summary dialog entirely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parse product feedback JSON in Langfuse scores to show human-readable reason/details instead of raw JSON - Add ?? 'other' fallback in toMinimalFeedback for when tag is missing - Relax toMinimalFeedback guard to only require rating (not tag) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-cards-default-open Show cBioAgent examples by default
Adds dd-trace as a root dependency so NODE_OPTIONS=--require dd-trace/init can auto-instrument Anthropic/OpenAI SDK calls at pod startup, enabling token and cost tracking in the Datadog LLM Observability dashboard. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: update package-lock.json for dd-trace
…BioPortal#20) * Resolve any Lucide icon by name for conversation-starter categories Replace the hardcoded 6-name categoryIcons map with a Lucide namespace lookup. Kebab/snake/PascalCase input all resolve to the matching Lucide component (book-open → BookOpen, info → Info, etc.). Bundle: lucide-react is namespace-imported (dynamic key lookup defeats tree-shaking), so the full set lands in the bundle. * unused-packages: whitelist dd-trace (loaded via NODE_OPTIONS at runtime)
PR cBioPortal#20 used `import * as LucideIcons from 'lucide-react'` to resolve any icon by name. That defeated tree-shaking and pulled the entire library into the http-client chunk alongside axios, where a circular-init order manifested as `Cannot access 'r' before initialization` (the icon factory `r` referenced before module init) — a blank page on chat.cbioportal.org. Switch to explicit named imports for a curated set of ~30 icons that cover the practical category labels we use. The kebab/snake → PascalCase resolver is preserved, just looking up in a literal registry instead of the namespace object. If a new icon is needed, add it to the named-import list.
…ioPortal#22) The previous fix added `Map` and `Navigation` as plain named imports from lucide-react. Both shadow JS globals at module scope, and the unrelated `useGetAssistantDocsQuery({ select: data => new Map(...) })` call in the same file ended up invoking lucide's `Map` icon (a forwardRef) as a constructor — production crashed with `TypeError: pa is not a constructor` from React Router. Alias them as MapIcon / NavigationIcon so the global `Map` constructor stays reachable. Category-icon resolution still works via the PascalCase registry key ("Map" → MapIcon).
…ortal#23) Replace the curated 30-icon named-import registry with the official dynamic loader. Each category icon is fetched as its own async chunk only when first rendered, keyed by the kebab-case Lucide name ("map-pin", "heart-pulse", "stethoscope", …). - No more hand-maintaining a list of allowed icons in code; configs can use any Lucide name. - No risk of namespace shadowing — icons never appear at module scope. - lucide-react no longer lands in the http-client / index chunks; each used icon is its own ~1KB lazy chunk, downloaded once per visit. Input is normalized to kebab via a simple regex (`BookOpen` → `book-open`, `book_open` → `book-open`, `Book Open` → `book-open`). Unknown names fall back to Search; the Suspense fallback also renders Search while a chunk loads.
…rmaid (cBioPortal#24) This reverts commit 47f4017.
Co-authored-by: ForisKuang <ForisKuang@users.noreply.github.com> Co-authored-by: Ino de Bruijn <ino@ino.bio>
* Brand the SPA shell as cBioChat with the cbioportal icon Replace the static <title>, meta description, and favicon set so the HTML shell — what Google's crawler and link-preview cards see — reads "cBioChat" with the cbioportal cell icon instead of LibreChat. The runtime APP_TITLE env continues to drive in-app strings (page title after first render, login alt text, etc.) and can override this per deployment if needed. Favicon source: src/rootImages/cbioportal_icon.png from cbioportal-frontend (32x32, the official site favicon). Generated: - favicon-16x16.png (Lanczos downscale) - favicon-32x32.png (passthrough) - apple-touch-icon-180x180.png (Lanczos upscale) - icon-192x192.png (Lanczos upscale) - maskable-icon.png (192x192 with PWA 80% safe-zone padding) Upscaling a 32x32 source is inherently soft; cbioportal's brand assets don't ship a higher-resolution raster of the icon-only mark. Acceptable trade-off versus shipping LibreChat's default branding. * Regenerate favicons from the cBioPortal org GitHub avatar (74x74) The earlier set was upscaled from cbioportal-frontend's 32x32 raster, which was visibly soft at 180/192. The cBioPortal GitHub org avatar is 74x74 — still small but a 2.6x upscale to 192x192 instead of 6x, notably crisper. Source: https://avatars.githubusercontent.com/u/9876251?v=4 If a higher-resolution raster of the icon-only mark turns up, swap the source and rerun the same generator. * Maskable icon: keep 512x512 to match the webmanifest declaration vite.config.ts declares this asset as sizes:"512x512" purpose:"maskable". The earlier commit shrank it to 192x192 alongside the regular icon set, which would fail PWA validators (Lighthouse / Chrome DevTools "PWA" audit) and could cause Android home-screen installs to fall back to the non-maskable icon. Rebuild at 512x512 from the 74x74 GitHub avatar source, with the content placed inside the inner 80% safe zone (~410x410) and 20% padding around it so the OS's home-screen mask (circle, squircle, rounded square, …) doesn't clip the icon. * Maskable icon: opaque white background A transparent canvas leaks through whatever shape mask the OS applies on Android home-screen install (circle, squircle, rounded square, …), producing ragged edges or background color showing through. The maskable spec assumes the icon is opaque — the OS draws the shape *over* the pixels rather than under them. Fill the 512x512 canvas solid white before pasting the icon. The icon's own background is already white, so the safe-zone padding now visually matches the inner content instead of being see-through. * Apple-touch icon: add safe-zone padding and opaque white background iOS clips apple-touch-icons into a rounded square (~20% corner radius) when added to the home screen, and the surface should be opaque (iOS doesn't show through transparent regions). Without padding the cbioportal cells were getting clipped at the corners. Same treatment as the maskable icon: inner content at 80% (144x144 of 180x180), centered, on a solid white canvas. Visually consistent with LibreChat's original — feather inside a circle with similar margin. * icon-192: match the safe-zone padding + opaque white treatment Keep the same shape language across the PWA icon set — inner 80% content on a solid white canvas. Matches LibreChat's original (feather-in-circle with padding) and stays visually consistent with the apple-touch and maskable icons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ggle
- Remove unreachable ExecuteCode, CodeAnalyze, and RetrievalCall returns
in Part.tsx — the early-return block (toggle OFF) and the
showReasoningActions guard (toggle ON) made them both inaccessible
- Drop the now-unused ExecuteCode, CodeAnalyze, and RetrievalCall imports
- Exclude agent-transfer tool calls (LC_TRANSFER_TO_*) from the toggle
hide block so AgentHandoff always renders, matching its pre-toggle
behaviour as a user-facing navigation event
- Remove tautological {showReasoningActions && <ToolCall>} in the
web_search branch — that branch is only reachable when the toggle is
already ON
- Initialize prevShowInfoRef to showReasoningActions (not false) in
ToolCall.tsx to prevent a spurious expand animation on mount when the
toggle is ON from a persisted localStorage value
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ns-toggle feat: Add reasoning & actions visibility toggle
…serialized users
When `req.user` is a plain object deserialized from a Passport session
(rather than a live Mongoose document), the `id` virtual getter is absent
from the prototype chain. `createSafeUser` previously skipped the `id`
field entirely in that case, leaving `{{LIBRECHAT_USER_ID}}` unresolved in
MCP server headers and preventing `usr.id` from appearing in Datadog traces.
Add a fallback that derives `id` from `_id` whenever the `id` field isn't
captured via the `in` operator. Also adds `createSafeUser` unit tests,
including an end-to-end test that verifies `{{LIBRECHAT_USER_ID}}` resolves
through `processMCPEnv` when `id` comes from the `_id` fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er-propagation fix: ensure user id propagates to MCP request headers from session-deserialized users
… path (cBioPortal#30) PR cBioPortal#29 fixed createSafeUser to populate `id` from `_id` for session- deserialized users, but the MCP tool-call runtime path (MCP.js -> MCPManager.callTool -> processMCPEnv) passes the raw config.configurable.user and never calls createSafeUser. The placeholder resolver read user.id only, so those users shipped the literal "{{LIBRECHAT_USER_ID}}" header (confirmed in prod Datadog spans as enduser.id="{{LIBRECHAT_USER_ID}}"). Fall back to `_id` inside processUserPlaceholders so id resolves for every caller without needing createSafeUser. Adds regression tests exercising processMCPEnv directly with a session-shaped user (no createSafeUser). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…/_id (cBioPortal#31) PRs cBioPortal#29 and cBioPortal#30 populate the MCP x-user-id header from req.user.id / req.user._id, but in the agent->MCP flow config.configurable.user = createSafeUser(req.user) and req.user carries neither the id virtual nor _id. The user's ObjectId IS available as the agent client's this.user (and userId in openai/responses), so {{LIBRECHAT_USER_ID}} still shipped unresolved (confirmed in prod: enduser.id= "{{LIBRECHAT_USER_ID}}" with both prod+beta already running cBioPortal#30). Add an optional fallbackId to createSafeUser and pass this.user / userId at the config-building sites so the header resolves regardless of req.user's shape. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…g path PRs cBioPortal#29-cBioPortal#31 fixed user-id propagation into MCP headers across the agent client, openai controller, and responses controller, but loadTools() (api/app/clients/tools/util/handleTools.js) builds the MCP `user` object from `options.req?.user` alone via createSafeUser. When req.user is a plain object deserialized from the passport session (no `id` virtual, no `_id`), that resolves to no usable id — even though loadTools is already given the caller's resolved id via its own `user` param (ToolService.js calls loadTools({ user: req.user.id, ... }), and that same `user` value is already used directly for auth lookups a few lines up). Confirmed live in production: mcp.tool/* spans on cbioportal-mcp still shipped enduser.id="{{LIBRECHAT_USER_ID}}" well after PR cBioPortal#31 deployed. Thread the existing `user` param through as createSafeUser's fallbackId, matching the pattern PR cBioPortal#31 established elsewhere. Adds regression tests exercising the MCP 'all'-tools branch (the one that calls createMCPTools), verifying the fallback fires when req.user has neither id nor _id, and that an explicit req.user.id still takes precedence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Merged
2 tasks
Member
|
superseded by: #33 |
inodb
added a commit
that referenced
this pull request
Aug 7, 2026
Second follow-up to the v0.8.7 rebase. This test asserted the pre-fork
behavior ("we only check id") — but our fork intentionally treats _id
as a fallback for id via the createSafeUser + loadTools chain from PRs
#29/#30/#31 and #32 (the fix this PR ports). The whole point of that
chain is that passport-deserialized session user objects carry _id but
not id, and MCP tool calls previously shipped the literal placeholder
{{LIBRECHAT_USER_ID}} instead of the resolved id.
Update the "_id only" assertion to reflect what our fork actually
does. The paired "id takes precedence when both are present" assertion
below is already correct and unchanged.
inodb
added a commit
that referenced
this pull request
Aug 7, 2026
…g path (v0.8.7 port) (#33) * fix(mcp): resolve {{LIBRECHAT_USER_ID}} in loadTools' MCP tool-loading path PRs #29-#31 fixed user-id propagation into MCP headers across the agent client, openai controller, and responses controller, but loadTools() (api/app/clients/tools/util/handleTools.js) builds the MCP `user` object from `options.req?.user` alone via createSafeUser. When req.user is a plain object deserialized from the passport session (no `id` virtual, no `_id`), that resolves to no usable id — even though loadTools is already given the caller's resolved id via its own `user` param (ToolService.js calls loadTools({ user: req.user.id, ... }), and that same `user` value is already used directly for auth lookups a few lines up). Confirmed live in production: mcp.tool/* spans on cbioportal-mcp still shipped enduser.id="{{LIBRECHAT_USER_ID}}" well after PR #31 deployed. Thread the existing `user` param through as createSafeUser's fallbackId, matching the pattern PR #31 established elsewhere. Adds regression tests exercising the MCP 'all'-tools branch (the one that calls createMCPTools), verifying the fallback fires when req.user has neither id nor _id, and that an explicit req.user.id still takes precedence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Include conversation_starters in the VIEW safe-list test expectation Follow-up to the v0.8.7 rebase: our fork intentionally exposes `conversation_starters` on the list-agents endpoint via commit b044fe8 ("Fix agent conversation starters not returned by list endpoint") so that the landing page can render them without a second per-agent fetch. Upstream added a "mass assignment protection" security test that asserts the exact safe-list of returned fields — that test doesn't know about our extension and fails on any PR against v0.8.7-custom-v1. Rather than undo b044fe8's projection change (which would break the landing UI), extend the test's expected list to reflect what our fork intentionally returns. Distinct from the mcp user-id fix that opened this PR, but folded in so v0.8.7-custom-v1 gets a clean CI baseline. * mcp.spec: expect _id to resolve LIBRECHAT_USER_ID (fork behavior) Second follow-up to the v0.8.7 rebase. This test asserted the pre-fork behavior ("we only check id") — but our fork intentionally treats _id as a fallback for id via the createSafeUser + loadTools chain from PRs #29/#30/#31 and #32 (the fix this PR ports). The whole point of that chain is that passport-deserialized session user objects carry _id but not id, and MCP tool calls previously shipped the literal placeholder {{LIBRECHAT_USER_ID}} instead of the resolved id. Update the "_id only" assertion to reflect what our fork actually does. The paired "id takes precedence when both are present" assertion below is already correct and unchanged. --------- Co-authored-by: ForisKuang <ForisKuang@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PRs #29, #30, and #31 progressively fixed
{{LIBRECHAT_USER_ID}}resolution across thecreateSafeUserhelper, the MCP tool-call runtime path, and the agent-client config-building sites — but one call site was missed:loadTools()inapi/app/clients/tools/util/handleTools.js.That function receives the caller's already-resolved user ID as its own
userparameter (the same value used two lines later forloadAuthValues({ userId: user, ... })), but its MCP-tool-loading section builds theuserobject handed tocreateMCPTools/createMCPToolfromoptions.req?.useralone:When
options.req.useris a plain object deserialized from the passport session (noidvirtual, no_id) — the same class of object PR #29 addressed —safeUser.idstays unset and{{LIBRECHAT_USER_ID}}ships unresolved to the MCP server'sx-user-idheader, exactly like the bug #30/#31 fixed elsewhere.Confirmed live in production, well after #31 deployed:
service:cbioportal-mcpspans on the cbioagent Datadog dashboard still showenduser.id="{{LIBRECHAT_USER_ID}}"for real tool calls (clickhouse_run_select_query,read_guide, etc.) routed through this path.Fix
Thread the existing
userparam through ascreateSafeUser'sfallbackId, matching the pattern #31 established at every other call site:Test plan
handleTools.test.jsexercising the MCP'all'-tools branch (the one that callscreateMCPTools): one confirming the fallback resolves the id whenreq.userhas neitheridnor_id, one confirming an explicitreq.user.idstill takes precedenceReceived: {"user": {"email": ..., "provider": "local"}}— noidkey) and passes with ithandleTools.test.jssuite passes (14/14)eslintclean on both changed files🤖 Generated with Claude Code