feat(mcp): improve remote ingest guidance
Make upload-ticket auth the default remote-agent path by returning complete upload URLs and updating service output/docs. Add validate_upload and preview_upload_metadata for lightweight file checks and exact sha256 duplicate warnings before ingest. Expose version, capability schema, file-index freshness, queue state, and known limitations through get_mcp_capabilities. Validation: python3 -m unittest discover -v; python3 -m compileall obsidian_wiki tests scripts; git diff --check.
This commit is contained in:
@@ -597,6 +597,7 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
project_root=root / "project",
|
||||
ingest_runtime_root=root / "ingest-runtime",
|
||||
query_runtime_root=root / "query-runtime",
|
||||
public_base_url="https://wiki.example.test/mcp",
|
||||
ingest_manager=IngestManager(
|
||||
vault_path=root / "vault",
|
||||
project_root=root / "project",
|
||||
@@ -618,13 +619,28 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
capabilities = FakeMCP.tools["get_mcp_capabilities"]()
|
||||
|
||||
self.assertEqual(capabilities["server_name"], "wiki-file-assets")
|
||||
self.assertIn("server_version", capabilities)
|
||||
self.assertEqual(capabilities["capabilities_schema_version"], 1)
|
||||
self.assertEqual(capabilities["tools_version"], 1)
|
||||
self.assertEqual(capabilities["enabled_tools"], ["file-assets", "ingest", "query"])
|
||||
self.assertIn("file_index", capabilities)
|
||||
self.assertIn("record_count", capabilities["file_index"])
|
||||
self.assertIn("last_rebuild_at", capabilities["file_index"])
|
||||
self.assertTrue(capabilities["query"]["enabled"])
|
||||
self.assertEqual(capabilities["query"]["preferred_tool"], "ask_wiki")
|
||||
self.assertIn("ask_wiki", capabilities["query"]["tools"])
|
||||
self.assertTrue(capabilities["query"]["supports_structured_result"])
|
||||
self.assertTrue(capabilities["query"]["known_limitations"])
|
||||
self.assertTrue(capabilities["ingest"]["enabled"])
|
||||
self.assertEqual(capabilities["ingest"]["preferred_upload_auth"], "upload_ticket")
|
||||
self.assertEqual(capabilities["ingest"]["http_upload_path"], "/mcp-ingest/uploads")
|
||||
self.assertEqual(capabilities["ingest"]["http_upload_url"], "https://wiki.example.test/mcp-ingest/uploads")
|
||||
self.assertEqual(capabilities["ingest"]["preferred_text_tool"], "ingest_text_note")
|
||||
self.assertIn("validate_upload", capabilities["ingest"]["tools"])
|
||||
self.assertIn("preview_upload_metadata", capabilities["ingest"]["tools"])
|
||||
self.assertIn("queue", capabilities["ingest"])
|
||||
self.assertIn("last_ingest_at", capabilities["ingest"])
|
||||
self.assertTrue(capabilities["ingest"]["known_limitations"])
|
||||
self.assertIn("search_file_assets", capabilities["file_assets"]["tools"])
|
||||
self.assertNotIn("vault_path", json.dumps(capabilities))
|
||||
self.assertNotIn("runtime", json.dumps(capabilities).lower())
|
||||
@@ -753,6 +769,7 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
enable_ingest=True,
|
||||
project_root=root / "project",
|
||||
ingest_runtime_root=root / "runtime",
|
||||
public_base_url="https://wiki.example.test/mcp",
|
||||
)
|
||||
ticket = FakeMCP.tools["create_ingest_upload_ticket"](
|
||||
expires_seconds=60,
|
||||
@@ -766,11 +783,80 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
self.assertIn("upload_token", ticket)
|
||||
self.assertEqual(ticket["token_header"], "X-Obsidian-Wiki-Upload-Token")
|
||||
self.assertEqual(ticket["upload_path"], "/mcp-ingest/uploads")
|
||||
self.assertEqual(ticket["upload_url"], "https://wiki.example.test/mcp-ingest/uploads")
|
||||
self.assertIn("X-Obsidian-Wiki-Upload-Token", ticket["curl_example"])
|
||||
self.assertNotIn("Authorization: Bearer", ticket["curl_example"])
|
||||
self.assertEqual(ticket["max_files"], 1)
|
||||
self.assertFalse(ticket["start_job"])
|
||||
self.assertIn("ingest_text_note", instructions["preferred_method"])
|
||||
self.assertEqual(instructions["preferred_upload_auth"], "upload_ticket")
|
||||
self.assertEqual(instructions["http_upload"]["url"], "https://wiki.example.test/mcp-ingest/uploads")
|
||||
self.assertIn("create_ingest_upload_ticket", instructions["preferred_method"])
|
||||
self.assertEqual(instructions["http_upload"]["bearer_auth"], "admin_or_local_only")
|
||||
self.assertNotIn("Authorization: Bearer", instructions["http_upload"]["curl_example"])
|
||||
self.assertEqual(instructions["direct_text_tool"]["name"], "ingest_text_note")
|
||||
|
||||
def test_ingest_tools_validate_upload_and_preview_metadata(self):
|
||||
class FakeMCP:
|
||||
tools = {}
|
||||
|
||||
def __init__(self, _name, **_kwargs):
|
||||
pass
|
||||
|
||||
def tool(self):
|
||||
def decorator(func):
|
||||
self.tools[func.__name__] = func
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def resource(self, *_args, **_kwargs):
|
||||
def decorator(func):
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def prompt(self, *_args, **_kwargs):
|
||||
def decorator(func):
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tmp,
|
||||
patch.dict("sys.modules", {"mcp.server.fastmcp": type("Mod", (), {"FastMCP": FakeMCP})()}),
|
||||
):
|
||||
root = Path(tmp)
|
||||
manager = IngestManager(
|
||||
vault_path=root / "vault",
|
||||
project_root=root / "project",
|
||||
runtime_root=root / "runtime",
|
||||
max_file_bytes=20,
|
||||
)
|
||||
mcp_server.create_server(
|
||||
vault_path=root / "vault",
|
||||
enable_ingest=True,
|
||||
project_root=root / "project",
|
||||
ingest_runtime_root=root / "runtime",
|
||||
ingest_manager=manager,
|
||||
)
|
||||
validate = FakeMCP.tools["validate_upload"](
|
||||
files=[
|
||||
{"file_name": "ok.pdf", "size_bytes": 2, "sha256": "a" * 64},
|
||||
{"file_name": "large.pdf", "size_bytes": 21, "sha256": "b" * 64},
|
||||
],
|
||||
project="bjd",
|
||||
doc_type_hint="drawing",
|
||||
)
|
||||
upload = manager.create_completed_upload("ok.pdf", b"ok", project="bjd", doc_type_hint="drawing")
|
||||
preview = FakeMCP.tools["preview_upload_metadata"]([upload["upload_id"]])
|
||||
|
||||
self.assertFalse(validate["ok"])
|
||||
self.assertEqual(validate["files"][0]["status"], "ok")
|
||||
self.assertEqual(validate["files"][1]["status"], "error")
|
||||
self.assertTrue(preview["ok"])
|
||||
self.assertEqual(preview["uploads"][0]["file_name"], "ok.pdf")
|
||||
self.assertEqual(preview["next_action"], "start_ingest_job")
|
||||
|
||||
def test_ingest_text_note_tool_creates_completed_markdown_upload(self):
|
||||
class FakeMCP:
|
||||
tools = {}
|
||||
|
||||
Reference in New Issue
Block a user