icarus_canister/
endpoints.rs1use crate::state::STATE;
7use candid::{CandidType, Deserialize, Principal};
8use icarus_core::protocol::{IcarusMetadata, ToolMetadata};
9
10pub fn icarus_metadata() -> IcarusMetadata {
12 STATE.with(|s| {
13 let state = s.borrow();
14 if let Some(state) = state.as_ref() {
15 IcarusMetadata {
16 version: state.config.get().version.clone(),
17 canister_id: ic_cdk::id(),
18 tools: state
19 .tools
20 .iter()
21 .map(|(name, tool_state)| {
22 ToolMetadata {
23 name: name.clone(),
24 candid_method: name.clone(), is_query: tool_state.is_query,
26 description: format!("{} tool", name), parameters: vec![], }
29 })
30 .collect(),
31 }
32 } else {
33 IcarusMetadata {
34 version: "1.0.0".to_string(),
35 canister_id: ic_cdk::id(),
36 tools: vec![],
37 }
38 }
39 })
40}
41
42#[derive(CandidType, Deserialize)]
44pub struct HttpRequest {
45 pub method: String,
46 pub url: String,
47 pub headers: Vec<(String, String)>,
48 pub body: Vec<u8>,
49}
50
51#[derive(CandidType)]
53pub struct HttpResponse {
54 pub status_code: u16,
55 pub headers: Vec<(String, String)>,
56 pub body: Vec<u8>,
57}
58
59pub fn http_request(_req: HttpRequest) -> HttpResponse {
61 let metadata = icarus_metadata();
62
63 let tools_html: String = metadata
65 .tools
66 .iter()
67 .map(|tool| {
68 format!(
69 r#"<div class="tool">
70 <strong>{}</strong> ({})
71 <br><small>{}</small>
72 </div>"#,
73 tool.name,
74 if tool.is_query { "query" } else { "update" },
75 tool.description
76 )
77 })
78 .collect::<Vec<_>>()
79 .join("\n");
80
81 let html = format!(
82 r#"<!DOCTYPE html>
83<html>
84<head>
85 <title>Icarus Canister</title>
86 <style>
87 body {{
88 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
89 max-width: 800px;
90 margin: 50px auto;
91 padding: 20px;
92 background: #f5f5f5;
93 }}
94 .container {{
95 background: white;
96 padding: 30px;
97 border-radius: 10px;
98 box-shadow: 0 2px 10px rgba(0,0,0,0.1);
99 }}
100 h1 {{
101 color: #333;
102 margin-bottom: 10px;
103 }}
104 .info {{
105 background: #e7f3ff;
106 padding: 15px;
107 border-radius: 5px;
108 border-left: 4px solid #2196F3;
109 margin: 20px 0;
110 }}
111 code {{
112 background: #f0f0f0;
113 padding: 2px 5px;
114 border-radius: 3px;
115 font-family: "Courier New", monospace;
116 }}
117 .tool {{
118 margin: 10px 0;
119 padding: 10px;
120 background: #f9f9f9;
121 border-radius: 5px;
122 }}
123 </style>
124</head>
125<body>
126 <div class="container">
127 <h1>🚀 Icarus Canister</h1>
128 <p>This is a standard ICP canister built with Icarus SDK.</p>
129
130 <div class="info">
131 <strong>Canister ID:</strong> <code>{}</code><br>
132 <strong>Version:</strong> <code>{}</code><br>
133 <strong>Status:</strong> <span style="color: green;">✓ Running</span>
134 </div>
135
136 <h2>Available Tools</h2>
137 {}
138
139 <h2>Connect with Claude Desktop</h2>
140 <p>To use this canister with Claude Desktop, run:</p>
141 <code>icarus bridge start --canister-id {}</code>
142
143 <hr style="margin-top: 40px; border: none; border-top: 1px solid #eee;">
144 <p style="text-align: center; color: #666; font-size: 14px;">
145 Powered by <a href="https://icarus.dev" style="color: #2196F3;">Icarus SDK</a>
146 </p>
147 </div>
148</body>
149</html>"#,
150 ic_cdk::id().to_text(),
151 metadata.version,
152 tools_html,
153 ic_cdk::id().to_text()
154 );
155
156 HttpResponse {
157 status_code: 200,
158 headers: vec![
159 (
160 "Content-Type".to_string(),
161 "text/html; charset=UTF-8".to_string(),
162 ),
163 ("Cache-Control".to_string(), "no-cache".to_string()),
164 ],
165 body: html.into_bytes(),
166 }
167}
168
169pub fn get_owner() -> Principal {
171 crate::state::get_owner()
172}