ios-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. ios_mcp-0.1.0/.env.example +102 -0
  2. ios_mcp-0.1.0/.gitignore +23 -0
  3. ios_mcp-0.1.0/ARCHITECTURE.md +212 -0
  4. ios_mcp-0.1.0/LICENSE +21 -0
  5. ios_mcp-0.1.0/PKG-INFO +336 -0
  6. ios_mcp-0.1.0/README.md +302 -0
  7. ios_mcp-0.1.0/SAFETY.md +110 -0
  8. ios_mcp-0.1.0/SECURITY.md +47 -0
  9. ios_mcp-0.1.0/agent/README.md +93 -0
  10. ios_mcp-0.1.0/agent/pyproject.toml +35 -0
  11. ios_mcp-0.1.0/docs/adr/0001-the-agent-is-a-peer-of-the-server.md +71 -0
  12. ios_mcp-0.1.0/docs/adr/0002-no-explicit-planner.md +98 -0
  13. ios_mcp-0.1.0/docs/adr/0003-no-cross-session-memory.md +107 -0
  14. ios_mcp-0.1.0/docs/adr/0004-no-subagents.md +107 -0
  15. ios_mcp-0.1.0/docs/adr/0005-langgraph-core-not-deepagents.md +72 -0
  16. ios_mcp-0.1.0/docs/adr/0006-the-agent-calls-iossession-directly.md +81 -0
  17. ios_mcp-0.1.0/docs/adr/0007-report-unreachable-content-rather-than-reaching-it.md +86 -0
  18. ios_mcp-0.1.0/docs/adr/0008-the-front-end-is-a-third-distribution.md +101 -0
  19. ios_mcp-0.1.0/docs/adr/0009-the-eval-trend-is-committed.md +81 -0
  20. ios_mcp-0.1.0/docs/adr/README.md +25 -0
  21. ios_mcp-0.1.0/docs/real-device-setup.md +194 -0
  22. ios_mcp-0.1.0/docs/tool-reference.md +96 -0
  23. ios_mcp-0.1.0/ios_mcp/__init__.py +0 -0
  24. ios_mcp-0.1.0/ios_mcp/__main__.py +96 -0
  25. ios_mcp-0.1.0/ios_mcp/actions/__init__.py +0 -0
  26. ios_mcp-0.1.0/ios_mcp/actions/idempotency.py +58 -0
  27. ios_mcp-0.1.0/ios_mcp/actions/result.py +128 -0
  28. ios_mcp-0.1.0/ios_mcp/actions/stabilize.py +88 -0
  29. ios_mcp-0.1.0/ios_mcp/config.py +278 -0
  30. ios_mcp-0.1.0/ios_mcp/devices/__init__.py +0 -0
  31. ios_mcp-0.1.0/ios_mcp/devices/base.py +143 -0
  32. ios_mcp-0.1.0/ios_mcp/devices/devicectl.py +126 -0
  33. ios_mcp-0.1.0/ios_mcp/devices/discovery.py +253 -0
  34. ios_mcp-0.1.0/ios_mcp/devices/doctor.py +587 -0
  35. ios_mcp-0.1.0/ios_mcp/devices/pool.py +215 -0
  36. ios_mcp-0.1.0/ios_mcp/devices/ports.py +45 -0
  37. ios_mcp-0.1.0/ios_mcp/devices/real_device.py +468 -0
  38. ios_mcp-0.1.0/ios_mcp/devices/shell.py +79 -0
  39. ios_mcp-0.1.0/ios_mcp/devices/simulator.py +348 -0
  40. ios_mcp-0.1.0/ios_mcp/devices/tunnel.py +75 -0
  41. ios_mcp-0.1.0/ios_mcp/errors.py +211 -0
  42. ios_mcp-0.1.0/ios_mcp/perception/__init__.py +0 -0
  43. ios_mcp-0.1.0/ios_mcp/perception/digest.py +897 -0
  44. ios_mcp-0.1.0/ios_mcp/perception/refs.py +92 -0
  45. ios_mcp-0.1.0/ios_mcp/perception/resolve.py +280 -0
  46. ios_mcp-0.1.0/ios_mcp/perception/roles.py +204 -0
  47. ios_mcp-0.1.0/ios_mcp/perception/vision.py +85 -0
  48. ios_mcp-0.1.0/ios_mcp/policy/__init__.py +0 -0
  49. ios_mcp-0.1.0/ios_mcp/policy/audit.py +110 -0
  50. ios_mcp-0.1.0/ios_mcp/policy/faults.py +126 -0
  51. ios_mcp-0.1.0/ios_mcp/policy/gate.py +169 -0
  52. ios_mcp-0.1.0/ios_mcp/policy/redact.py +55 -0
  53. ios_mcp-0.1.0/ios_mcp/policy/secrets.py +56 -0
  54. ios_mcp-0.1.0/ios_mcp/py.typed +0 -0
  55. ios_mcp-0.1.0/ios_mcp/server/__init__.py +0 -0
  56. ios_mcp-0.1.0/ios_mcp/server/annotations.py +39 -0
  57. ios_mcp-0.1.0/ios_mcp/server/app.py +67 -0
  58. ios_mcp-0.1.0/ios_mcp/server/context.py +88 -0
  59. ios_mcp-0.1.0/ios_mcp/server/errors.py +37 -0
  60. ios_mcp-0.1.0/ios_mcp/server/resources.py +124 -0
  61. ios_mcp-0.1.0/ios_mcp/server/tools_act.py +252 -0
  62. ios_mcp-0.1.0/ios_mcp/server/tools_app.py +139 -0
  63. ios_mcp-0.1.0/ios_mcp/server/tools_env.py +100 -0
  64. ios_mcp-0.1.0/ios_mcp/server/tools_perceive.py +152 -0
  65. ios_mcp-0.1.0/ios_mcp/server/tools_session.py +54 -0
  66. ios_mcp-0.1.0/ios_mcp/session.py +949 -0
  67. ios_mcp-0.1.0/ios_mcp/wda/__init__.py +0 -0
  68. ios_mcp-0.1.0/ios_mcp/wda/client.py +271 -0
  69. ios_mcp-0.1.0/ios_mcp/wda/models.py +153 -0
  70. ios_mcp-0.1.0/ios_mcp/wda/session.py +393 -0
  71. ios_mcp-0.1.0/pyproject.toml +149 -0
  72. ios_mcp-0.1.0/scripts/prepare_wda.sh +205 -0
  73. ios_mcp-0.1.0/scripts/start_tunnel.sh +38 -0
  74. ios_mcp-0.1.0/server.json +46 -0
  75. ios_mcp-0.1.0/tui/README.md +152 -0
  76. ios_mcp-0.1.0/tui/pyproject.toml +33 -0
@@ -0,0 +1,102 @@
1
+ # Copy to `.env` and edit. `.env` is gitignored; this file is not.
2
+ #
3
+ # Precedence, highest first: a real environment variable, then `.env`, then
4
+ # `ios-mcp.toml`, then the defaults in code. So a shell export or CI setting
5
+ # overrides this file without anyone editing it.
6
+ #
7
+ # Nested settings use a double underscore: IOS_MCP_<SECTION>__<FIELD>.
8
+ # Every value below is the default unless noted, so an untouched copy of this
9
+ # file changes nothing.
10
+
11
+ # ---------------------------------------------------------------------------
12
+ # Model provider (agent only). The base install commits to no vendor; install
13
+ # the matching extra, e.g. `uv sync --extra openai`.
14
+ # ---------------------------------------------------------------------------
15
+ IOS_AGENT_PROVIDER=anthropic
16
+ IOS_AGENT_MODEL=claude-opus-5
17
+ IOS_AGENT_MAX_TOKENS=16000
18
+ # Anthropic only, sent as output_config. Skipped for every other provider.
19
+ IOS_AGENT_EFFORT=medium
20
+ # Deliberately unset. Claude Opus 5, Opus 4.8, Opus 4.7 and Sonnet 5 reject it
21
+ # with a 400; set it only on a provider that accepts it.
22
+ # IOS_AGENT_TEMPERATURE=0.7
23
+ # Turns before the loop gives up on a goal.
24
+ IOS_AGENT_MAX_STEPS=24
25
+ # Anything this project has not heard of, passed to the provider untouched.
26
+ # Applied last, so it overrides everything above. JSON.
27
+ # IOS_AGENT_EXTRA={"reasoning_effort": "high"}
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Provider API keys are NOT read by this project. Each vendor SDK resolves its
31
+ # own, and it does so better than we could: the Anthropic SDK also accepts an
32
+ # auth token or an `ant auth login` profile. Listed here only so the full set
33
+ # of knobs is in one place.
34
+ # ---------------------------------------------------------------------------
35
+ # ANTHROPIC_API_KEY=
36
+ # OPENAI_API_KEY=
37
+ # GOOGLE_API_KEY=
38
+ # GROQ_API_KEY=
39
+ # (ollama runs locally and needs no key)
40
+
41
+ # What the eval suite prices tokens at, per million. Defaults are Claude
42
+ # Opus 5's rates; nothing here can know what another vendor charges.
43
+ # IOS_AGENT_USD_PER_MTOK_IN=5.0
44
+ # IOS_AGENT_USD_PER_MTOK_OUT=25.0
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Safety. Read SAFETY.md before turning any of this off. Doing so is a
48
+ # reasonable choice for a simulator running tests, and is not a reasonable one
49
+ # for a device carrying someone's real accounts.
50
+ # ---------------------------------------------------------------------------
51
+ IOS_MCP_POLICY__ENABLED=true
52
+ IOS_MCP_POLICY__CONFIRM_DESTRUCTIVE=true
53
+ IOS_MCP_POLICY__MAX_CONSECUTIVE_FAILURES=5
54
+ IOS_MCP_POLICY__LOOP_DETECTION_WINDOW=6
55
+ # Empty means "anything not blocked". Set it to restrict the agent to one app.
56
+ # IOS_MCP_POLICY__APP_ALLOWLIST=["com.apple.Preferences"]
57
+
58
+ # Secrets are referenced, never inlined. `ios_type_secret` resolves a reference
59
+ # from the macOS keychain first, falling back to IOS_MCP_SECRET_<REF_UPPER>.
60
+ # The value never reaches a prompt, a tool result, or the audit trail.
61
+ # security add-generic-password -s ios-mcp -a icloud-password -w
62
+ # IOS_MCP_SECRET_ICLOUD_PASSWORD=
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Device and WebDriverAgent
66
+ # ---------------------------------------------------------------------------
67
+ # UDID or a name substring. Unset picks the best ready device, preferring a
68
+ # simulator: acting on a real phone should be deliberate.
69
+ # IOS_MCP_DEFAULT_DEVICE=
70
+ # Point at a WebDriverAgent someone else is running (a device farm, a phone on
71
+ # Wi-Fi, a runner started by hand). When set, nothing is launched or torn down.
72
+ # IOS_MCP_WDA__BASE_URL=
73
+ # Free Apple accounts cannot sign com.facebook.*, so the runner bundle id is
74
+ # configurable for exactly that reason.
75
+ # IOS_MCP_WDA__BUNDLE_ID=com.facebook.WebDriverAgentRunner.xctrunner
76
+ # A physical device needs a longer startup budget than a simulator.
77
+ # IOS_MCP_WDA__STARTUP_TIMEOUT_S=90
78
+
79
+ # `simctl boot` starts the runtime, not the window: without this a simulator
80
+ # runs headlessly and nothing appears on the Mac. Turn it off for CI, where a
81
+ # window is at best pointless.
82
+ IOS_MCP_SIMULATOR__SHOW_WINDOW=true
83
+
84
+ # A device snapshot costs about 3.7s against well under a second on a
85
+ # simulator, so max_wait_s must exceed stable_samples snapshots or a real
86
+ # device times out on every action.
87
+ # IOS_MCP_STABILIZE__MAX_WAIT_S=20
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Perception. These decide what an agent loop costs.
91
+ # ---------------------------------------------------------------------------
92
+ IOS_MCP_DIGEST__TOKEN_BUDGET=1500
93
+ # Depth 20 silently loses a third of a Settings screen; past 30 is free,
94
+ # because the round trip dominates rather than the traversal.
95
+ IOS_MCP_SNAPSHOT__MAX_DEPTH=50
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # Server transport
99
+ # ---------------------------------------------------------------------------
100
+ IOS_MCP_SERVER__TRANSPORT=stdio
101
+ # IOS_MCP_SERVER__PORT=8765
102
+ IOS_MCP_LOG_LEVEL=INFO
@@ -0,0 +1,23 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .mypy_cache/
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .coverage
11
+ htmlcov/
12
+ .env
13
+ .artifacts/
14
+ vendor/wda/*.app/
15
+ vendor/wda/*.ipa
16
+
17
+ # WebDriverAgent checkout and build products (rebuild with scripts/prepare_wda.sh)
18
+ vendor/wda/WebDriverAgent/
19
+ vendor/wda/DerivedData/
20
+ vendor/wda/*.xctestrun
21
+
22
+ # Personal, machine-local Claude Code settings
23
+ .claude/settings.local.json
@@ -0,0 +1,212 @@
1
+ # Architecture
2
+
3
+ Six layers. Layers 1 to 4 are a plain async library with no MCP imports, so an
4
+ agent framework can import `IosSession` directly and skip the protocol
5
+ round-trip on latency-critical steps. The MCP server is one consumer of that
6
+ library, not the thing itself, and `ios_agent` is a second one: a peer of the
7
+ server rather than a layer above it, since the policy gate lives inside
8
+ `IosSession` and both pass through it identically. `ios_tui` is a third
9
+ distribution on top of the agent, and it is separate for a concrete reason
10
+ rather than a tidy one: the agent is held to a seven-module public surface, and
11
+ a front end needs device discovery and the doctor, which are outside it. See
12
+ `docs/adr/0008-the-front-end-is-a-third-distribution.md`.
13
+ `tests/unit/test_layering.py` enforces the no-MCP-imports rule, the agent's
14
+ public surface, and the front end's Textual-free event path statically.
15
+
16
+ ```
17
+ ios_tui <- terminal front end
18
+ |
19
+ MCP clients (Claude Code, Claude Desktop, an iOS app) ios_agent
20
+ | |
21
+ v |
22
+ 5 mcp FastMCP: tools, resources, prompts, transports | <- only package importing MCP
23
+ | |
24
+ +---------------------+--------------------+
25
+ v
26
+ 4 actions act, stabilize, re-observe; idempotency keys (IosSession)
27
+ 3 perception accessibility tree -> UI Digest, stable refs, resolution <- most of the value
28
+ 2 wda typed WebDriverAgent client, session settings, auto-heal
29
+ 1 devices SimulatorAdapter | RealDeviceAdapter, device pool
30
+ |
31
+ iOS Simulator iPhone (USB / Wi-Fi)
32
+
33
+ 6 policy approval gate, secret injection, redaction, audit, kill switch
34
+ constructed inside IosSession, so every action passes through
35
+ it whichever consumer started the call
36
+ ```
37
+
38
+ ## 1. Device fabric (`ios_mcp/devices/`)
39
+
40
+ One `DeviceAdapter` protocol, two implementations, so nothing above branches on
41
+ device kind.
42
+
43
+ `SimulatorAdapter` uses `simctl` for what WebDriverAgent cannot do: privacy
44
+ permissions, location, appearance, freezing the status bar for reproducible
45
+ screenshots. `RealDeviceAdapter` uses `go-ios`, including the RemoteXPC tunnel
46
+ that iOS 17+ requires.
47
+
48
+ Starting WebDriverAgent differs fundamentally between the two, and this is not
49
+ a detail that can be abstracted away:
50
+
51
+ - **Simulator**: must go through `xcodebuild test-without-building` against a
52
+ prebuilt `.xctestrun`. `simctl launch` on the `.xctrunner` app cannot work,
53
+ because the app aborts immediately without an XCTestConfiguration that only
54
+ `testmanagerd` supplies.
55
+ - **Device**: `go-ios` drives `testmanagerd` directly, so a signed prebuilt
56
+ runner app can be launched without Xcode.
57
+
58
+ `DevicePool` keeps one live session per device, allocates ports, and reclaims
59
+ idle leases. It is where a remote or device-farm adapter would drop in.
60
+
61
+ ## 2. WebDriverAgent client (`ios_mcp/wda/`)
62
+
63
+ A typed `httpx` client. No Appium.
64
+
65
+ Two things happen here that everything above depends on. **Session settings**
66
+ are pushed on every new session: WebDriverAgent's default snapshot depth
67
+ produces trees far too large and slow for an agent loop. **Auto-heal** catches
68
+ a dead session or a crashed runner, rebuilds it, restores the foreground app,
69
+ and retries once, reporting `recovered` rather than raising. An agent driving a
70
+ phone for minutes will outlive at least one runner crash, and making it reason
71
+ about that wastes its turns.
72
+
73
+ ## 3. Perception (`ios_mcp/perception/`)
74
+
75
+ Where the cost of the whole system is decided. Raw page source for a 200-row
76
+ list is roughly 37,000 tokens; the digest is 329.
77
+
78
+ The pipeline is prune, deduplicate, collapse repeats, score, budget, assign
79
+ refs. Two rules do most of the work:
80
+
81
+ - **Wrapper collapse.** Containers that exist only to hold other nodes are
82
+ dropped, as are StaticText children echoing their row's own label. iOS emits
83
+ that echo for nearly every list row.
84
+ - **Coincidence merge.** iOS reports one control several times: a row-wide
85
+ element carrying the label, and the control itself at the trailing edge.
86
+ These merge into one node that takes *semantics from the labelled node and
87
+ geometry from the tighter one*. That split is not cosmetic. Keeping the row's
88
+ rect aims taps at the label, where a switch ignores them, so `set_value`
89
+ reports success while changing nothing.
90
+
91
+ Overlap is proportional rather than strict containment, because real iOS rects
92
+ do not nest: a Settings toggle reports `x=305 w=63` inside a row at `x=36
93
+ w=330`, overhanging its own parent by two points.
94
+
95
+ **Resolution** runs a six-tier chain server-side, so a retry costs zero model
96
+ tokens where bouncing back to the model costs a whole turn:
97
+
98
+ | Tier | How |
99
+ |---|---|
100
+ | `exact` | ref from the last digest, verified to still denote the same element |
101
+ | `id` | stale ref, re-found by accessibility identifier |
102
+ | `label+role` | stale ref, re-found by label and role |
103
+ | `proximity` | stale ref, nearest same-role element |
104
+ | `text-exact` / `id-exact` | plain-language target, exact match |
105
+ | `text-partial` / `text-fuzzy` | substring, then edit distance |
106
+
107
+ Refs are positional, so inserting a row shifts every ref below it. Tier 1
108
+ therefore verifies identity before trusting position; acting on the wrong
109
+ control is the worst failure this system can have. `RefTable` records only what
110
+ the agent was actually shown, which is what makes that check possible.
111
+
112
+ Ambiguity is refused, not guessed: two identically labelled Delete buttons
113
+ raise with both candidates listed.
114
+
115
+ **Fingerprints** hash structure, state and title. Positions round to 4px so
116
+ animation jitter does not register while real shifts do. The title is included
117
+ because a split view keeps most of the screen identical during navigation.
118
+
119
+ ## 4. Actions (`ios_mcp/actions/`, `ios_mcp/session.py`)
120
+
121
+ Every action is act, stabilize, re-observe in one call. An observe/act/observe
122
+ loop costs two round-trips per step; folding the observation in halves that.
123
+
124
+ - **Fresh geometry.** Every action re-reads the screen before acting rather
125
+ than trusting the last observation. Seconds of model latency sit between
126
+ observe and act, and anything that moved in between would send the tap to
127
+ whatever now occupies those coordinates.
128
+ - **Stabilization** polls the fingerprint until it repeats, rather than
129
+ sleeping a fixed interval. An optional baseline keeps it polling while the
130
+ screen still matches its pre-action state, so a slow transition is not
131
+ mistaken for an action that did nothing.
132
+ - **Deltas.** When the screen is structurally similar the result is a diff
133
+ (`~ e2 switch "Bold Text" =1 (was 0)`); a genuine navigation returns the
134
+ whole screen.
135
+ - **Idempotency keys** make a repeat a no-op returning the original result.
136
+ This exists from the first action because agent frameworks re-run the node an
137
+ interrupt was raised from, and retrofitting act-once semantics after callers
138
+ exist is not safely possible.
139
+
140
+ ## 5. MCP surface (`ios_mcp/server/`)
141
+
142
+ Roughly 30 semantic tools, not a mirror of WebDriverAgent's HTTP routes: a
143
+ large set of confusable tools measurably degrades tool selection. A test
144
+ asserts the count stays bounded.
145
+
146
+ Digest payloads omit the structured element list by default. It duplicates the
147
+ rendered text at roughly twice the tokens, and both would be pushed into the
148
+ model's context, so sending both means paying twice for one screen.
149
+
150
+ Errors reach the client as structured JSON carrying a code, a hint, and
151
+ candidate elements, so a failed resolution tells the agent what it could have
152
+ picked instead.
153
+
154
+ ## 6. Policy (`ios_mcp/policy/`)
155
+
156
+ See [SAFETY.md](SAFETY.md).
157
+
158
+ ## The agent (`agent/ios_agent/`)
159
+
160
+ A separate distribution in the uv workspace, depending on `ios-mcp` and never
161
+ the reverse. It is a second consumer of `IosSession`, not a seventh layer: the
162
+ policy gate is constructed inside the session, so an agent passes through it on
163
+ the same code path the server does.
164
+
165
+ The loop is one model node, one tool node, and an edge back. Eight tools rather
166
+ than the server's thirty, because a large confusable set degrades tool
167
+ selection. The provider is chosen through `init_chat_model`, so the package
168
+ commits to no vendor.
169
+
170
+ A destructive action **pauses the graph** with a LangGraph `interrupt()` rather
171
+ than being decided on the phone owner's behalf. The gate classifies before
172
+ acting, so nothing has touched the device at the moment of the pause, and with
173
+ no approver attached everything destructive is refused: an unanswerable
174
+ question is not consent.
175
+
176
+ Resuming re-runs the entire node, which is why every action's idempotency key
177
+ is its **tool call id** rather than a step counter. A counter yields a
178
+ different key on the re-run, misses the cache, and taps the device twice; a
179
+ test asserts this by putting a harmless tap in the same turn as one that needs
180
+ approval and counting what reaches the device.
181
+
182
+ What is *not* in it is the interesting part. Planning, subagents and
183
+ cross-session memory were each specified, measured, and rejected with numbers;
184
+ see [docs/adr/](docs/adr/). The one mechanism that survived is verification,
185
+ and it exists because actions already return the screen they produced: it reads
186
+ `screen_changed` and the delta to notice an action the device ignored, and
187
+ costs zero additional observations. That cut actions across the task set by 38%.
188
+
189
+ The reason it can cost nothing is layer 4's design. Because an action folds its
190
+ resulting screen into the response, the agent spends exactly one observation
191
+ per task, which is the same number a hand-written oracle needs. The
192
+ observation-per-step cost that this whole stack was optimised against turned
193
+ out to be already at its floor before any agent pillar was built.
194
+
195
+ ## Testing
196
+
197
+ - `tests/unit` (326) runs against an in-process fake WebDriverAgent, so it
198
+ needs no Xcode, simulator, or phone. It also enforces the layering statically:
199
+ only `ios_mcp/server` may import MCP, and `ios_agent` may touch only a listed
200
+ public surface.
201
+ - `tests/integration` (13) runs against a real simulator, including a chaos
202
+ test that kills the runner mid-flow.
203
+ - `tests/evals` (11 flows) measures tokens, wall time, action count, and
204
+ resolution-tier distribution. These four numbers are the product metrics.
205
+ - `tests/evals/agent` measures the agent itself: 10 goal-directed tasks against
206
+ a scripted device, each declaring the action count a hand-written oracle
207
+ needs. Three tasks inject failures taken from real hardware. A `model` tier
208
+ puts a real model in the loop, and a `simulator` tier runs it against real
209
+ iOS.
210
+
211
+ The fakes cannot catch everything. Every bug in the Phase 5 commit was found
212
+ only by running against a real device, which is why the eval suite exists.
ios_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ehsan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.