Files
codex/prs/bolinfest/study/PR-1571-study.md
2025-09-02 15:17:45 -07:00

131 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
**DOs**
- **Use Deterministic Qualification:** Prefer the simplest stable name; only add context when needed.
```rust
use sha1::{Digest, Sha1};
const MAX: usize = 64;
const DELIM: &str = "__";
fn qualify_name(server: &str, tool: &str, used: &mut std::collections::HashSet<String>) -> String {
// 1) tool
if tool.len() <= MAX && used.insert(tool.to_string()) {
return tool.to_string();
}
// 2) server__tool
let st = format!("{server}{DELIM}{tool}");
if st.len() <= MAX && used.insert(st.clone()) {
return st;
}
// 3) prefix + hash(server__tool), still <= MAX
let mut hasher = Sha1::new();
hasher.update(st.as_bytes());
let hash = format!("{:x}", hasher.finalize()); // 40 hex chars
let extra = 0; // or 1 if you want an extra '_' between prefix and hash
let keep = MAX.saturating_sub(hash.len() + extra);
let prefix: String = tool.chars().take(keep).collect();
let qualified = if extra == 1 {
format!("{prefix}_{hash}")
} else {
format!("{prefix}{hash}")
};
used.insert(qualified.clone());
qualified
}
```
- **Keep a Canonical Map:** Store `ToolInfo` by qualified name; use it for lookups (dont re-parse).
```rust
#[derive(Clone)]
struct ToolInfo { server: String, tool: String, /* Tool object, etc. */ }
struct McpConnectionManager {
tools: std::collections::HashMap<String, ToolInfo>, // qualified -> info
}
impl McpConnectionManager {
fn parse_tool_name(&self, name: &str) -> Option<(String, String)> {
self.tools.get(name).map(|t| (t.server.clone(), t.tool.clone()))
}
}
```
- **Cap Names and Use Allowed Delimiters:** Enforce 64-char max and stick to `a-zA-Z0-9_-` (e.g., `"__"`).
```rust
const MAX_TOOL_NAME_LENGTH: usize = 64;
const MCP_TOOL_NAME_DELIMITER: &str = "__";
fn is_valid_mcp_server_name(s: &str) -> bool {
!s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
}
```
- **Warn-And-Skip Duplicates:** Deduplicate consistently; log and continue instead of panicking.
```rust
use tracing::warn;
let mut used = std::collections::HashSet::new();
let name = qualify_name(&server, &tool, &mut used);
if !used.insert(name.clone()) {
warn!("skipping duplicated tool {}", name);
// continue;
}
```
- **Stabilize Ordering in UX/Tests:** Dont rely on async join order or `HashMap` iteration; sort keys when needed.
```rust
let mut keys: Vec<_> = manager.tools.keys().cloned().collect();
keys.sort(); // stable output / assertions
assert!(!keys.is_empty());
```
- **Test The Edge Cases:** Cover unique, duplicate, and long-name scenarios with focused unit tests.
```rust
#[test]
fn qualify_unique_and_long_names() {
let mut used = std::collections::HashSet::new();
let a = qualify_name("s1", "tool", &mut used);
let b = qualify_name("s2", "tool", &mut used); // forces fallback naming
assert_ne!(a, b);
assert!(a.len() <= 64 && b.len() <= 64);
}
```
**DONTs**
- **Dont Add Unrelated Changes:** Keep the PR focused on MCP tool naming; avoid drive-by edits elsewhere.
```diff
- // codex-cli/bin/codex.js (unrelated)
- const wantsNative = fs.existsSync(path.join(__dirname, "use-native")) ||
+ // Remove unrelated change from this PR
```
- **Dont Use Random Suffixes:** Avoid per-run randomness; names must be stable across rollouts.
```rust
// Bad: non-deterministic
let suffix = format!("{}", std::time::SystemTime::now().elapsed().unwrap().as_nanos());
let name = format!("{}__{}_{suffix}", server, tool);
```
- **Dont Re-Parse Strings:** Stop splitting on delimiters to recover server/tool; look up the map instead.
```rust
// Bad
let (server, tool) = name.split_once("__").unwrap();
// Good
let (server, tool) = manager.parse_tool_name(name).expect("unknown tool name");
```
- **Dont Rely On Insertion/Join Order:** Async listing order is not stable; never assert on raw map iteration.
```rust
// Bad: order-dependent UI/test
for (k, _) in &manager.tools { println!("{k}"); } // nondeterministic
```
- **Dont Exceed Limits Or Use Invalid Delimiters:** Respect 64-char max and allowed charset; avoid exotic separators.
```rust
// Bad: too long, invalid delimiter
let name = format!("{server}||{}", tool); // '||' not allowed, length unchecked
```
- **Dont Panic On Collisions:** Collisions happen; handle gracefully with deterministic dedup and logging.
```rust
// Bad
panic!("tool name collision for '{}'", name);
```