modelcontextprotocol / modelcontextprotocol/ext-apps
Chat composer is displaced upward after interacting with a fullscreen MCP App and sending a message
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.9k
- Forks
- 387
- Avg merge
- 3h 21m
- Merged PRs (30d)
- 6
Description
Summary
In Claude Desktop, clicking a control inside a fullscreen MCP App leaves the
conversation in a state where every subsequent message moves the chat composer up away
from the bottom of the window. The displacement accumulates and can carry the composer
out of the viewport.
The behavior is host-side, and it reproduces with the debug-server example in this
repository, copied without modification. Filed here because the
getting-started page
points to this repository for MCP Apps issues.
Three measured properties:
- Until the app is clicked, messages do not displace the composer at all, at any scroll
depth. - After the app is clicked, each message adds displacement equal to about half the
scroll height that message adds. - The click itself displaces nothing, and returning focus to the composer does not undo
the state.
Leaving fullscreen, or switching to another conversation, restores the composer.
Environment
- Claude Desktop 1.52386.6.0 (Microsoft Store package) on Windows 11
- Reproduced with
examples/debug-server, copied without modification and packaged as a
local.mcpbdesktop extension
Steps to reproduce
- Package
examples/debug-serveras a.mcpband install it in Claude Desktop. - In a new conversation, call its tool and put the app in fullscreen display mode.
- Send two or three messages without clicking anything inside the app. The composer
stays at the bottom of the window. - Click any control inside the app. The composer does not move yet.
- Send another message. The composer moves up.
- Send further messages. The composer moves up again each time.
Measurements
Taken in the Claude Desktop DevTools console (Help > Troubleshooting > Enable Developer
Mode, then Ctrl+Shift+I), with the unmodified debug-server app fullscreen:
(() => {
const c = document.querySelector('[data-chat-input-container]');
const s = c?.closest('[data-autoscroll-container]');
return {
composerGap: Math.round(innerHeight - c.getBoundingClientRect().bottom),
scrollTop: Math.round(s?.scrollTop ?? -1),
scrollHeight: Math.round(s?.scrollHeight ?? -1),
clientHeight: Math.round(s?.clientHeight ?? -1),
};
})()
composerGap is the distance from the bottom of the window to the bottom of the
composer. When the composer is correctly placed, it is 0.
| Sample | composerGap | scrollTop | scrollHeight | clientHeight |
|---|---|---|---|---|
| Messages sent, app untouched | 0 | 837 | 2735 | 1522 |
| Messages sent, app untouched | 0 | 2580 | 4103 | 1522 |
| Messages sent, app untouched | 0 | 2984 | 4507 | 1522 |
| After clicking in the app, then sending | 941 | 4278 | 5912 | 1522 |
The first three samples show that scroll depth alone does not displace the composer:
scrollTop more than triples while composerGap stays at 0. The fourth sample differs
from them by one action, a click inside the app's iframe.
The accumulation, sampled every 500ms
Once the app has been clicked, a timer logging composerGap, scrollTop and
document.activeElement through two further sends gives this sequence, condensed to the
points where a value changes:
| Event | composerGap | scrollTop | activeElement |
|---|---|---|---|
| Start of recording, app already clicked once | 1193 | 4666 | DIV[chat-input] |
| Click inside the app, held for about 10s | 1193 | 4666 | IFRAME |
| Focus returns to the composer | 1193 | 4666 | DIV[chat-input] |
| Send a message | 1380 | 5041 | DIV[chat-input] |
| Send a message | 1569 | 5418 | DIV[chat-input] |
Two things follow from that sequence.
The displacement advances only when a message is sent, not when the app is clicked.
composerGap is unchanged across the whole period when activeElement is the app's
iframe, and both increases happen while focus is on the composer.
Each increase is close to half the scroll height the message adds. The first send adds
375 to scrollTop and 187 to composerGap, and the second adds 377 and 189.
Additional observations from a second app
A separate MCP App, which presents many controls and so is clicked on constantly, shows
the displacement throughout a fullscreen session. In that app composerGap grew to
3828 in a long conversation, and in a new conversation it tracked the scroller's
scrollTop closely, measuring 281 against a scrollTop of 273.
In every sample from both apps the composer reports position: sticky, so the
displacement is not the sticky positioning being dropped. The app container reports
position: fixed, and dismissing the app changed scrollHeight by 61px, so the app's
frame is not contributing to the conversation scroller's content height.
Impact
A fullscreen MCP App becomes hard to use alongside the conversation, because any use of
the app displaces the composer and every subsequent message displaces it further. An app
with controls cannot avoid the trigger.
Leaving and re-entering fullscreen restores the composer temporarily until the next message is sent, as does switching
conversations.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the behavior with the unmodified examples/debug-server packaged as a .mcpb in Claude Desktop, then use the described DevTools measurements to compare composerGap and scroll state before and after an iframe click. Done means identifying a repository-side path to address the host-side displacement, or confirming that no fix can be made here.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100