Skip to content

Commit 82fcaf5

Browse files
jackwildmangithub-actions[bot]
authored andcommitted
fix(everyrow-mcp, everyrow-cc): remove session link/URL from all code paths (#4958)
## Summary - Remove all references to session URLs/links from everyrow-mcp and everyrow-cc source code, tests, templates, and Cypress E2E tests - The "session link" concept is outdated — session URLs are no longer surfaced to users in tool results, HTML widgets, or progress responses - 26 files changed across both projects: MCP server tools/templates/result store, CC agent stream parsers, frontend types, and Cypress tests ## Test plan - [x] everyrow-mcp tests pass (416 passed, 18 skipped) - [x] everyrow-cc agent tests pass (112 passed) - [ ] Verify Cypress replay tests still pass with updated `waitForSessionActivation` helper 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Sourced from commit bec0807f9edd04c0e8b144bc2c728f64214ca568
1 parent bba1cd8 commit 82fcaf5

11 files changed

Lines changed: 25 additions & 316 deletions

File tree

futuresearch-mcp/src/futuresearch_mcp/app.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ async def no_auth_http_lifespan(_server: FastMCP):
125125
- Be concise. Keep summaries to 1-2 sentences. Do not output markdown tables, \
126126
bullet lists of data rows, JSON, or CSV in chat — the user can see results \
127127
directly. Only render a table if the user explicitly asks for one.
128-
- Do not share session URLs with the user unless they explicitly ask for one.
129128
- Never guess or fabricate results — always wait for the task to complete.
130129
- For small datasets (<= {settings.auto_page_size_threshold} rows), prefer passing `data` directly.
131130
- For larger datasets, use `futuresearch_upload_data` to get an artifact_id first.

futuresearch-mcp/src/futuresearch_mcp/result_store.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,12 @@ def _build_result_response(
165165
columns: list[str],
166166
offset: int,
167167
page_size: int,
168-
session_url: str = "",
169168
poll_token: str = "",
170169
mcp_server_url: str = "",
171170
artifact_id: str = "",
172171
*,
173172
requested_page_size: int | None = None,
174173
skip_widget: bool = False,
175-
skip_session: bool = False,
176174
) -> CallToolResult:
177175
"""Build a CallToolResult with separate content and structuredContent.
178176
@@ -186,8 +184,6 @@ def _build_result_response(
186184
and is used in the "next page" hint so the server can re-clamp
187185
independently on each call.
188186
"""
189-
if skip_session:
190-
session_url = ""
191187
visible_columns = [c for c in columns if c not in _LLM_STRIP_FIELDS]
192188
col_names = _format_columns(visible_columns)
193189
hint_page_size = (
@@ -214,8 +210,6 @@ def _build_result_response(
214210
"total": total,
215211
"fetch_full_results": True,
216212
}
217-
if session_url:
218-
structured["session_url"] = session_url
219213
if artifact_id:
220214
structured["artifact_id"] = artifact_id
221215
if poll_token:
@@ -295,7 +289,6 @@ async def try_cached_result(
295289
mcp_server_url: str = "",
296290
*,
297291
skip_widget: bool = False,
298-
skip_session: bool = False,
299292
) -> CallToolResult | None:
300293
cached_meta_raw = await redis_store.get_result_meta(task_id)
301294
if not cached_meta_raw:
@@ -345,13 +338,11 @@ async def try_cached_result(
345338
columns=meta["columns"],
346339
offset=min(offset, meta["total"]),
347340
page_size=effective_page_size,
348-
session_url=meta.get("session_url", ""),
349341
poll_token=poll_token or "",
350342
mcp_server_url=mcp_server_url,
351343
artifact_id=meta.get("artifact_id", ""),
352344
requested_page_size=page_size,
353345
skip_widget=skip_widget,
354-
skip_session=skip_session,
355346
)
356347

357348

@@ -360,12 +351,10 @@ async def try_store_result(
360351
df: pd.DataFrame,
361352
offset: int,
362353
page_size: int,
363-
session_url: str = "",
364354
mcp_server_url: str = "",
365355
artifact_id: str = "",
366356
*,
367357
skip_widget: bool = False,
368-
skip_session: bool = False,
369358
) -> CallToolResult:
370359
"""Store a DataFrame in Redis and return a paginated response."""
371360
try:
@@ -377,8 +366,6 @@ async def try_store_result(
377366

378367
# Store base metadata
379368
meta: dict[str, Any] = {"total": total, "columns": columns}
380-
if session_url:
381-
meta["session_url"] = session_url
382369
if artifact_id:
383370
meta["artifact_id"] = artifact_id
384371
await redis_store.store_result_meta(task_id, json.dumps(meta))
@@ -412,13 +399,11 @@ async def try_store_result(
412399
columns=columns,
413400
offset=clamped_offset,
414401
page_size=effective_page_size,
415-
session_url=session_url,
416402
poll_token=poll_token or "",
417403
mcp_server_url=mcp_server_url,
418404
artifact_id=artifact_id,
419405
requested_page_size=page_size,
420406
skip_widget=skip_widget,
421-
skip_session=skip_session,
422407
)
423408
except Exception:
424409
logger.exception("Failed to store results in Redis for task %s", task_id)

futuresearch-mcp/src/futuresearch_mcp/templates.py

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,7 @@
8282
.copy-modal-box textarea{width:100%;height:300px;font-family:monospace;font-size:12px;border:1px solid var(--border);border-radius:4px;padding:8px;background:var(--input-bg);color:var(--text);resize:vertical}
8383
.copy-modal-box .modal-btns{display:flex;gap:8px;justify-content:flex-end}
8484
.copy-modal-box button{padding:6px 16px;border:1px solid var(--border);border-radius:5px;background:var(--btn-bg);color:var(--btn-text);cursor:pointer;font-size:12px}
85-
.session-link{margin-bottom:6px;font-size:12px;display:flex;align-items:center;gap:8px}
86-
.session-link a{font-weight:500}
87-
.session-link .spacer{flex:1}
85+
.search-bar{margin-bottom:6px;display:flex;align-items:center;justify-content:flex-end}
8886
.col-resize-handle{position:absolute;top:0;right:-2px;width:4px;height:100%;cursor:col-resize;z-index:5;user-select:none}
8987
.col-resize-handle:hover{background:var(--accent);opacity:.3}
9088
body.col-resizing,body.col-resizing *{cursor:col-resize!important;user-select:none!important}
@@ -111,7 +109,7 @@
111109
.settings-drop input[type="radio"]{margin:0}
112110
.settings-drop .drop-sep{border-top:1px solid var(--border-light);margin:4px 0}
113111
</style></head><body>
114-
<div id="sessionLink" class="session-link"><span class="spacer"></span><input id="globalSearch" type="text" placeholder="Search all columns..."></div>
112+
<div class="search-bar"><input id="globalSearch" type="text" placeholder="Search all columns..."></div>
115113
<div id="toolbar">
116114
<span id="sum">Loading...</span>
117115
<button id="selAllBtn">Select all</button>
@@ -145,9 +143,8 @@
145143
const copyModal=document.getElementById("copyModal");
146144
const copyArea=document.getElementById("copyArea");
147145
const closeCopyModal=document.getElementById("closeCopyModal");
148-
const sessionLinkEl=document.getElementById("sessionLink");
149146
150-
let sessionUrl="",csvUrl="",pollToken="",downloadTokenUrl="";
147+
let csvUrl="",pollToken="",downloadTokenUrl="";
151148
const TRUNC=200;
152149
let didDrag=false;
153150
let copyFmt="csv";
@@ -674,7 +671,6 @@
674671
}
675672
return csvUrl;
676673
}
677-
function updateDownloadLink(){updateSessionLink();}
678674
document.getElementById("exportLink")?.addEventListener("click",async()=>{
679675
if(!csvUrl&&!downloadTokenUrl){showToast("No download link yet");return;}
680676
const url=await getFreshDownloadUrl();
@@ -719,26 +715,6 @@
719715
document.removeEventListener("mouseup",onRowResizeUp);
720716
}
721717
722-
/* --- session URL display --- */
723-
const linksEl=document.createElement("span");linksEl.id="sessionLinks";
724-
sessionLinkEl.insertBefore(linksEl,sessionLinkEl.querySelector(".spacer"));
725-
function updateSessionLink(){
726-
let h="";
727-
if(sessionUrl)h+='<a href="#" id="sessionOpenLink">Open FutureSearch session &#x2197;</a>';
728-
if(csvUrl){if(h)h+=" &nbsp;|&nbsp; ";h+='<a href="#" id="csvOpenLink">Download CSV &#x2913;</a>';}
729-
linksEl.innerHTML=h;
730-
document.getElementById("sessionOpenLink")?.addEventListener("click",e=>{
731-
e.preventDefault();
732-
if(!/^https?:\\/\\//i.test(sessionUrl))return;app.openLink({url:sessionUrl}).catch(()=>window.open(sessionUrl,"_blank"));
733-
});
734-
document.getElementById("csvOpenLink")?.addEventListener("click",async e=>{
735-
e.preventDefault();
736-
const url=await getFreshDownloadUrl();
737-
if(!url||!/^https?:\\/\\//i.test(url))return;
738-
app.openLink({url}).catch(()=>window.open(url,"_blank"));
739-
});
740-
}
741-
742718
/* --- data loading --- */
743719
async function fetchFullResultsWithFreshToken(hasPreview,total){
744720
const base=await getFreshDownloadUrl();
@@ -767,10 +743,9 @@
767743
if(!isWidget){return;}
768744
widgetActive=true;
769745
document.body.style.height="auto";document.body.style.overflow="visible";document.body.style.padding="12px";
770-
if(meta.session_url&&!sessionUrl){sessionUrl=meta.session_url;updateSessionLink();}
771746
if(meta.poll_token){pollToken=meta.poll_token;}
772747
if(meta.download_token_url){downloadTokenUrl=meta.download_token_url;}
773-
if(meta.csv_url){csvUrl=meta.csv_url;updateDownloadLink();}
748+
if(meta.csv_url){csvUrl=meta.csv_url;}
774749
if(meta.fetch_full_results){
775750
if(meta.preview)processData(meta.preview);
776751
fetchFullResultsWithFreshToken(!!meta.preview,meta.total);
@@ -815,13 +790,13 @@
815790
import{App}from"SCRIPT_SRC";
816791
const app=new App({name:"FutureSearch Session",version:"1.0.0"});
817792
const el=document.getElementById("c");
818-
let pollUrl=null,pollToken=null,pollTimer=null,sessionUrl="",wasDone=false;
793+
let pollUrl=null,pollToken=null,pollTimer=null,wasDone=false;
819794
function esc(s){const d=document.createElement("div");d.textContent=String(s);return d.innerHTML;}
820795
821796
app.ontoolresult=({content})=>{
822797
const t=content?.find(c=>c.type==="text");if(!t)return;
823798
try{
824-
const d=JSON.parse(t.text);sessionUrl=d.session_url||"";render(d);
799+
const d=JSON.parse(t.text);render(d);
825800
if(d.progress_url&&!pollTimer){pollUrl=d.progress_url;pollToken=d.poll_token||null;startPoll()}
826801
}catch{el.textContent=t.text}
827802
};
@@ -830,10 +805,9 @@
830805
const comp=d.completed||0,tot=d.total||0,fail=d.failed||0,run=d.running||0;
831806
const pend=Math.max(0,tot-comp-fail-run);
832807
const done=["completed","failed","revoked"].includes(d.status);
833-
const url=d.session_url||sessionUrl;
834808
const elapsed=d.elapsed_s||0;
835809
836-
let h=url?`<a href="#" class="session-open">Open FutureSearch session &#x2197;</a>`:"";
810+
let h="";
837811
838812
if(tot>0){
839813
const pDone=comp/tot*100,pRun=run/tot*100,pFail=fail/tot*100;
@@ -871,12 +845,6 @@
871845
872846
el.innerHTML=h;
873847
874-
const link=el.querySelector(".session-open");
875-
if(link){link.addEventListener("click",e=>{
876-
e.preventDefault();
877-
if(!/^https?:\\/\\//i.test(url))return;app.openLink({url:url}).catch(()=>window.open(url,"_blank"));
878-
});}
879-
880848
if(done&&!wasDone){wasDone=true;el.classList.add("flash")}
881849
if(done&&pollTimer){clearInterval(pollTimer);pollTimer=null}
882850
}

futuresearch-mcp/src/futuresearch_mcp/tool_helpers.py

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from futuresearch.generated.models.task_status import TaskStatus
2626
from futuresearch.generated.models.task_status_response import TaskStatusResponse
2727
from futuresearch.generated.types import Unset
28-
from futuresearch.session import get_session_url
2928
from mcp.server.auth.middleware.auth_context import get_access_token
3029
from mcp.server.fastmcp import Context
3130
from mcp.server.session import ServerSession
@@ -195,24 +194,8 @@ def is_internal_client() -> bool:
195194
return "futuresearch" in get_user_agent().lower()
196195

197196

198-
def _submission_text(
199-
label: str, session_url: str, task_id: str, session_id: str = ""
200-
) -> str:
197+
def _submission_text(label: str, task_id: str, session_id: str = "") -> str:
201198
"""Build human-readable text for submission tool results."""
202-
if settings.is_stdio:
203-
session_line = f"\nSession ID: {session_id}" if session_id else ""
204-
return dedent(f"""\
205-
{label}
206-
Session: {session_url}{session_line}
207-
Task ID: {task_id}
208-
209-
Immediately call futuresearch_progress(task_id='{task_id}').""")
210-
if is_internal_client():
211-
return dedent(f"""\
212-
{label}
213-
Task ID: {task_id}
214-
215-
Immediately call futuresearch_progress(task_id='{task_id}').""")
216199
session_line = f"\nSession ID: {session_id}" if session_id else ""
217200
return dedent(f"""\
218201
{label}{session_line}
@@ -253,7 +236,6 @@ async def _record_task_ownership(task_id: str, token: str) -> str:
253236

254237

255238
async def _submission_ui_json(
256-
session_url: str,
257239
task_id: str,
258240
total: int,
259241
poll_token: str,
@@ -262,7 +244,6 @@ async def _submission_ui_json(
262244
) -> str:
263245
"""Build JSON for the session MCP App widget."""
264246
data: dict[str, Any] = {
265-
"session_url": session_url,
266247
"task_id": task_id,
267248
"total": total,
268249
"status": "submitted",
@@ -290,7 +271,6 @@ async def _start_headless_summarizer(task_id: str, token: str) -> None:
290271
async def create_tool_response(
291272
*,
292273
task_id: str,
293-
session_url: str,
294274
label: str,
295275
token: str,
296276
total: int,
@@ -302,7 +282,7 @@ async def create_tool_response(
302282
Returns human-readable text in all modes, plus a widget JSON
303283
prepended in HTTP mode.
304284
"""
305-
text = _submission_text(label, session_url, task_id, session_id=session_id)
285+
text = _submission_text(label, task_id, session_id=session_id)
306286
main_content = TextContent(type="text", text=text)
307287
if settings.is_http:
308288
poll_token = await _record_task_ownership(task_id, token)
@@ -311,7 +291,6 @@ async def create_tool_response(
311291
# summaries without needing a frontend SSE connection.
312292
await _start_headless_summarizer(task_id, token)
313293
ui_json = await _submission_ui_json(
314-
session_url=session_url,
315294
task_id=task_id,
316295
total=total,
317296
poll_token=poll_token,
@@ -383,11 +362,6 @@ def is_terminal(self) -> bool:
383362
def task_type(self) -> PublicTaskType:
384363
return self._response.task_type
385364

386-
@computed_field
387-
@property
388-
def session_url(self) -> str:
389-
return get_session_url(self._response.session_id)
390-
391365
@computed_field
392366
@property
393367
def completed(self) -> int:

0 commit comments

Comments
 (0)