Skip to content

Commit 475da76

Browse files
rgambeegithub-actions[bot]
authored andcommitted
Remove session URLs from SDK (#7374)
These links don't work anymore. Two users have emailed us in the past week due to confusion about them. This PR removes them from our SDK and MCP code, as well as from our documentation. Once we have a way to view SDK sessions within the app, we can add expose session URLs again. Sourced from commit ec00176767da07586db37fa57d05083cac141b3e
1 parent 63e9ed6 commit 475da76

10 files changed

Lines changed: 16 additions & 82 deletions

File tree

.env.example

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,3 @@ FUTURESEARCH_API_KEY=
44

55
# Optional: API base URL (defaults to https://futuresearch.ai/api/v0/)
66
# FUTURESEARCH_API_URL=https://futuresearch.ai/api/v0/
7-
# Optional: App base URL (defaults to https://futuresearch.ai/)
8-
# FUTURESEARCH_APP_URL=https://futuresearch.ai/

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,14 @@ The same API researches, cleans, and joins datasets, which is often how a foreca
163163

164164
## Sessions
165165

166-
You can also use a session to output a URL to see the research and data processing in the [futuresearch.ai/app](https://futuresearch.ai/app) application, which streams the research and makes charts. Or you can use it purely as an intelligent data utility, and [chain intelligent pandas operations](https://futuresearch.ai/docs/chaining-operations) with normal pandas operations where LLMs are used to process every row.
166+
Group related operations into a session so their tasks are tracked together. You can also use FutureSearch purely as an intelligent data utility, and [chain intelligent pandas operations](https://futuresearch.ai/docs/chaining-operations) with normal pandas operations where LLMs are used to process every row.
167167

168168
```python
169169
from futuresearch import create_session
170170

171171
async with create_session(name="My Session") as session:
172-
print(f"View session at: {session.get_url()}")
172+
# All operations here share the same session
173+
...
173174
```
174175

175176
### Async operations

futuresearch-mcp/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Alternatively, install with pip (ideally in a venv) and use `"command": "futures
5252

5353
All operations follow an async pattern:
5454

55-
1. **Start** - Call an operation tool (e.g., `futuresearch_agent`) to start a task. Returns immediately with a task ID and session URL.
55+
1. **Start** - Call an operation tool (e.g., `futuresearch_agent`) to start a task. Returns immediately with a task ID.
5656
2. **Monitor** - Call `futuresearch_progress(task_id)` repeatedly to check status. The tool blocks ~12s to limit the polling rate.
5757
3. **Retrieve** - Once complete, call `futuresearch_results(task_id, output_path)` to save results to CSV.
5858

futuresearch-mcp/src/futuresearch_mcp/templates.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,6 @@
177177
.done-banner .banner-text{flex:1}
178178
.done-banner .banner-close{background:none;border:none;color:#fff;font-size:18px;cursor:pointer;padding:0 4px;line-height:1;opacity:.8}
179179
.done-banner .banner-close:hover{opacity:1}
180-
.session-link{margin-bottom:6px;font-size:11px;display:flex;align-items:center;gap:8px}
181-
.session-link a{font-weight:500}
182-
.session-link .spacer{flex:1}
183180
.col-resize-handle{position:absolute;top:0;right:-2px;width:4px;height:100%;cursor:col-resize;z-index:5;user-select:none}
184181
.col-resize-handle:hover{background:var(--accent);opacity:.3}
185182
body.col-resizing,body.col-resizing *{cursor:col-resize!important;user-select:none!important}
@@ -336,7 +333,7 @@
336333
let forecastMeta=null;
337334
let forecastRows=null;
338335
339-
let sessionUrl="",csvUrl="",pollToken="",downloadUrl="";
336+
let csvUrl="",pollToken="",downloadUrl="";
340337
const TRUNC=200;
341338
let didDrag=false;
342339
const copyFmt="csv";
@@ -559,13 +556,11 @@
559556
/* Forecast tasks get the card grid; the table is hidden. */
560557
showForecastUI();
561558
renderForecastCards(Array.isArray(data)?data:[data]);
562-
updateSessionLink();
563559
switchTab("results");
564560
}else{
565561
/* All other operation types keep the original table UI. */
566562
showResultsUI();
567563
processData(data);
568-
updateSessionLink();
569564
switchTab("results");
570565
}
571566
/* NOTE: we deliberately do NOT call app.sendMessage() or
@@ -1031,7 +1026,6 @@
10311026
activityTab.style.display="block";
10321027
resultsTab.style.display="none";
10331028
forecastTab.style.display="none";
1034-
sessionUrl=d.session_url||sessionUrl;
10351029
if(d.task_id)currentTaskId=d.task_id;
10361030
/* Extract task_id from progress_url as fallback */
10371031
if(!currentTaskId&&d.progress_url){const m=d.progress_url.match(/progress\\/([0-9a-f-]+)/);if(m)currentTaskId=m[1];}
@@ -1579,7 +1573,7 @@
15791573
}
15801574
return csvUrl;
15811575
}
1582-
function updateDownloadLink(){updateSessionLink();}
1576+
function updateDownloadLink(){}
15831577
document.getElementById("exportLink")?.addEventListener("click",()=>{
15841578
const url=getDownloadUrl();
15851579
if(!url){showToast("No download link yet");return;}
@@ -1623,7 +1617,6 @@
16231617
document.removeEventListener("mouseup",onRowResizeUp);
16241618
}
16251619
1626-
function updateSessionLink(){}
16271620
16281621
/* --- data loading (for standalone results entry) --- */
16291622
async function fetchFullResultsWithFreshToken(hasPreview,total){
@@ -1750,7 +1743,6 @@
17501743
const isWidget=meta.fetch_full_results||meta.preview||Array.isArray(meta);
17511744
if(!isWidget)return;
17521745
showResultsUI();
1753-
if(meta.session_url&&!sessionUrl){sessionUrl=meta.session_url;updateSessionLink();}
17541746
if(meta.poll_token){pollToken=meta.poll_token;}
17551747
if(meta.download_url){downloadUrl=meta.download_url;}
17561748
if(meta.csv_url){csvUrl=meta.csv_url;updateDownloadLink();}

futuresearch-mcp/src/futuresearch_mcp/tools.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ async def futuresearch_list_sessions(
16301630
) -> list[TextContent]:
16311631
"""List futuresearch sessions owned by the authenticated user (paginated).
16321632
1633-
Returns session names, IDs, timestamps, and dashboard URLs.
1633+
Returns session names, IDs, and timestamps.
16341634
Use this to find past sessions or check what's been run.
16351635
Results are paginated — 25 sessions per page by default.
16361636
"""
@@ -1676,8 +1676,7 @@ async def futuresearch_list_sessions(
16761676
lines.append(
16771677
f"- **{s.name}** (id: {s.session_id})\n"
16781678
f" Created: {s.created_at:%Y-%m-%d %H:%M UTC} | "
1679-
f"Updated: {s.updated_at:%Y-%m-%d %H:%M UTC}\n"
1680-
f" URL: {s.get_url()}"
1679+
f"Updated: {s.updated_at:%Y-%m-%d %H:%M UTC}"
16811680
)
16821681

16831682
has_more = (result.offset + result.limit) < result.total

futuresearch-mcp/tests/test_server.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,6 @@ def _make_mock_session(session_id=None):
163163
"""Create a mock Session."""
164164
session = MagicMock()
165165
session.session_id = session_id or uuid4()
166-
session.get_url.return_value = (
167-
f"https://futuresearch.ai/sessions/{session.session_id}"
168-
)
169166
return session
170167

171168

@@ -677,14 +674,12 @@ async def test_list_sessions_returns_sessions(self):
677674
name="My Session",
678675
created_at=datetime(2025, 6, 1, 12, 0, tzinfo=UTC),
679676
updated_at=datetime(2025, 6, 1, 13, 0, tzinfo=UTC),
680-
get_url=lambda: "https://futuresearch.ai/sessions/abc",
681677
),
682678
MagicMock(
683679
session_id=uuid4(),
684680
name="Another Session",
685681
created_at=datetime(2025, 6, 2, 10, 0, tzinfo=UTC),
686682
updated_at=datetime(2025, 6, 2, 11, 0, tzinfo=UTC),
687-
get_url=lambda: "https://futuresearch.ai/sessions/def",
688683
),
689684
]
690685

@@ -746,8 +741,8 @@ async def test_list_sessions_passes_client_and_pagination(self):
746741
mock_ls.assert_called_once_with(client=mock_client, offset=5, limit=10)
747742

748743
@pytest.mark.asyncio
749-
async def test_list_sessions_output_contains_urls_and_dates(self):
750-
"""Test that the formatted output includes URLs and timestamps."""
744+
async def test_list_sessions_output_contains_dates(self):
745+
"""Test that the formatted output includes timestamps."""
751746
mock_client = _make_mock_client()
752747
ctx = make_test_context(mock_client)
753748
session_id = uuid4()
@@ -757,7 +752,6 @@ async def test_list_sessions_output_contains_urls_and_dates(self):
757752
name="Pipeline Run",
758753
created_at=datetime(2025, 8, 15, 9, 30, tzinfo=UTC),
759754
updated_at=datetime(2025, 8, 15, 10, 45, tzinfo=UTC),
760-
get_url=lambda: f"https://futuresearch.ai/sessions/{session_id}",
761755
),
762756
]
763757

@@ -772,7 +766,6 @@ async def test_list_sessions_output_contains_urls_and_dates(self):
772766
assert "Pipeline Run" in text
773767
assert "2025-08-15 09:30 UTC" in text
774768
assert "2025-08-15 10:45 UTC" in text
775-
assert f"https://futuresearch.ai/sessions/{session_id}" in text
776769

777770
@pytest.mark.asyncio
778771
async def test_list_sessions_pagination_params(self):
@@ -803,7 +796,6 @@ async def test_list_sessions_shows_pagination_info(self):
803796
name=f"Session {i}",
804797
created_at=now,
805798
updated_at=now,
806-
get_url=lambda: "https://futuresearch.ai/sessions/x",
807799
)
808800
for i in range(10)
809801
]

skills/futuresearch-python/SKILL.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -644,18 +644,16 @@ Parameters: `task`, `input`, `effort_level` (LOW/MEDIUM/HIGH), `response_model`,
644644

645645
## Explicit Sessions
646646

647-
For multiple operations or when you need visibility into progress, use an explicit session:
647+
For multiple operations that should be grouped together, use an explicit session:
648648

649649
```python
650650
from futuresearch import create_session
651651

652652
async with create_session(name="My Session") as session:
653-
print(f"View session at: {session.get_url()}")
654653
# All operations here share the same session
654+
...
655655
```
656656

657-
Sessions are visible on the futuresearch.ai dashboard.
658-
659657
## Async Operations
660658

661659
All operations have `_async` variants for background processing. These need an explicit session since the task persists beyond the function call:
@@ -691,11 +689,10 @@ df = await fetch_task_data("12345678-1234-1234-1234-123456789abc")
691689
FutureSearch operations (classify, rank, dedupe, merge, forecast, agent) take 1-10+ minutes.
692690
All MCP tools use an async pattern:
693691

694-
1. Call the operation tool (e.g., `futuresearch_agent(...)`) to get task_id and session_url
695-
2. Share session_url with the user
696-
3. Call futuresearch_progress(task_id) — the tool handles pacing internally
697-
4. After each status update, immediately call futuresearch_progress again
698-
5. When status is "completed" or "failed", call futuresearch_results(task_id)
692+
1. Call the operation tool (e.g., `futuresearch_agent(...)`) to get a task_id
693+
2. Call futuresearch_progress(task_id) — the tool handles pacing internally
694+
3. After each status update, immediately call futuresearch_progress again
695+
4. When status is "completed" or "failed", call futuresearch_results(task_id)
699696

700697
## Chaining Operations
701698

src/futuresearch/constants.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import warnings
22
from typing import Any
33

4-
DEFAULT_FUTURESEARCH_APP_URL = "https://futuresearch.ai"
54
DEFAULT_FUTURESEARCH_API_URL = "https://futuresearch.ai/api/v0"
65

76

src/futuresearch/session.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import os
21
from collections.abc import AsyncGenerator
32
from contextlib import asynccontextmanager
43
from dataclasses import dataclass
54
from datetime import datetime
65
from uuid import UUID
76

87
from futuresearch.api_utils import create_client
9-
from futuresearch.constants import DEFAULT_FUTURESEARCH_APP_URL
108
from futuresearch.errors import _call_and_check
119
from futuresearch.generated.api.sessions import (
1210
create_session_endpoint_sessions_post,
@@ -18,13 +16,6 @@
1816
from futuresearch.generated.models.update_session import UpdateSession
1917

2018

21-
def get_session_url(session_id: UUID) -> str:
22-
base_url = (
23-
os.environ.get("FUTURESEARCH_APP_URL") or DEFAULT_FUTURESEARCH_APP_URL
24-
).rstrip("/")
25-
return f"{base_url}/sessions/{session_id}"
26-
27-
2819
@dataclass
2920
class SessionInfo:
3021
"""Summary of an existing session."""
@@ -34,10 +25,6 @@ class SessionInfo:
3425
created_at: datetime
3526
updated_at: datetime
3627

37-
def get_url(self) -> str:
38-
"""Get the URL to view this session in the web interface."""
39-
return get_session_url(self.session_id)
40-
4128

4229
@dataclass
4330
class SessionListResult:
@@ -56,10 +43,6 @@ def __init__(self, client: AuthenticatedClient, session_id: UUID):
5643
self.client = client
5744
self.session_id = session_id
5845

59-
def get_url(self) -> str:
60-
"""Get the URL to view this session in the web interface."""
61-
return get_session_url(self.session_id)
62-
6346

6447
@asynccontextmanager
6548
async def create_session(

tests/test_session.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
SessionInfo,
1919
SessionListResult,
2020
create_session,
21-
get_session_url,
2221
list_sessions,
2322
)
2423

@@ -31,7 +30,6 @@ def _wrap(parsed: Any, status_code: HTTPStatus = HTTPStatus.OK) -> Response[Any]
3130
@pytest.fixture(autouse=True)
3231
def mock_env(monkeypatch):
3332
monkeypatch.setenv("FUTURESEARCH_API_KEY", "test-key")
34-
monkeypatch.setenv("FUTURESEARCH_APP_URL", "https://futuresearch.ai")
3533

3634

3735
# --- SessionInfo ---
@@ -47,31 +45,6 @@ def test_fields(self):
4745
assert info.created_at == now
4846
assert info.updated_at == now
4947

50-
def test_get_url(self):
51-
sid = uuid.uuid4()
52-
now = datetime.now(UTC)
53-
info = SessionInfo(session_id=sid, name="Test", created_at=now, updated_at=now)
54-
assert info.get_url() == get_session_url(sid)
55-
assert str(sid) in info.get_url()
56-
57-
58-
class TestGetSessionUrl:
59-
def test_honours_futuresearch_app_url(self, monkeypatch):
60-
monkeypatch.setenv("FUTURESEARCH_APP_URL", "https://new.example.com")
61-
sid = uuid.uuid4()
62-
assert get_session_url(sid) == f"https://new.example.com/sessions/{sid}"
63-
64-
def test_ignores_removed_legacy_app_url(self, monkeypatch):
65-
monkeypatch.delenv("FUTURESEARCH_APP_URL", raising=False)
66-
monkeypatch.setenv("EVERYROW_APP_URL", "https://legacy.example.com")
67-
sid = uuid.uuid4()
68-
assert get_session_url(sid) == f"https://futuresearch.ai/sessions/{sid}"
69-
70-
def test_falls_back_to_default(self, monkeypatch):
71-
monkeypatch.delenv("FUTURESEARCH_APP_URL", raising=False)
72-
sid = uuid.uuid4()
73-
assert get_session_url(sid) == f"https://futuresearch.ai/sessions/{sid}"
74-
7548

7649
# --- Generated models ---
7750

0 commit comments

Comments
 (0)