@@ -70,8 +70,12 @@ def team_workspace() -> WorkspaceInfo:
7070@pytest .fixture
7171def route_workspaces (app ):
7272 @contextmanager
73- def route (* workspaces : WorkspaceInfo ):
74- with _workspace_routing (app , workspaces ):
73+ def route (* workspaces : WorkspaceInfo , forward_permalink_headers : bool = True ):
74+ with _workspace_routing (
75+ app ,
76+ workspaces ,
77+ forward_permalink_headers = forward_permalink_headers ,
78+ ):
7579 yield
7680
7781 return route
@@ -117,7 +121,12 @@ def _save_permalink_config(
117121
118122
119123@contextmanager
120- def _workspace_routing (app , workspaces : Iterable [WorkspaceInfo ]):
124+ def _workspace_routing (
125+ app ,
126+ workspaces : Iterable [WorkspaceInfo ],
127+ * ,
128+ forward_permalink_headers : bool = True ,
129+ ):
121130 """Route MCP tool HTTP calls through an ASGI-backed cloud workspace seam."""
122131 workspace_list = tuple (workspaces )
123132 workspace_ids = {workspace .tenant_id for workspace in workspace_list }
@@ -128,10 +137,11 @@ async def workspace_provider():
128137 @asynccontextmanager
129138 async def factory (workspace : str | None = None ):
130139 assert workspace is None or workspace in workspace_ids
140+ headers = workspace_permalink_headers () if forward_permalink_headers else {}
131141 async with HttpxAsyncClient (
132142 transport = ASGITransport (app = app ),
133143 base_url = "http://test" ,
134- headers = workspace_permalink_headers () ,
144+ headers = headers ,
135145 ) as inner :
136146 yield inner
137147
@@ -461,3 +471,39 @@ async def test_team_workspace_permalink_routes_to_specific_workspace(
461471
462472 workspace_read = await _read_json (mcp_server , identifier = expected_permalink )
463473 assert workspace_read ["permalink" ] == expected_permalink
474+
475+
476+ @pytest .mark .asyncio
477+ async def test_write_note_by_project_id_qualifies_permalink_when_headers_not_forwarded (
478+ mcp_server ,
479+ test_project ,
480+ app_config ,
481+ team_workspace ,
482+ route_workspaces ,
483+ ):
484+ """MCP writes should return self-routing IDs even if the API omits slug headers."""
485+ _save_permalink_config (app_config , include_project = True , default_project = None )
486+
487+ title = "Project Id Workspace Permalink"
488+ short_permalink = "permalink-suite/project-id-workspace-permalink"
489+ expected_permalink = f"{ team_workspace .slug } /{ test_project .name } /{ short_permalink } "
490+
491+ with route_workspaces (team_workspace , forward_permalink_headers = False ):
492+ write_payload = await _call_json (
493+ mcp_server ,
494+ "write_note" ,
495+ {
496+ "project_id" : test_project .external_id ,
497+ "title" : title ,
498+ "directory" : "permalink-suite" ,
499+ "content" : f"# { title } \n \n Project ID workspace body." ,
500+ "output_format" : "json" ,
501+ },
502+ )
503+ assert write_payload ["permalink" ] == expected_permalink
504+
505+ workspace_read = await _read_json (
506+ mcp_server ,
507+ identifier = f"memory://{ write_payload ['permalink' ]} " ,
508+ )
509+ assert workspace_read ["title" ] == title
0 commit comments